@optique/core 0.10.7 → 1.0.0-dev.1109
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/README.md +4 -6
- package/dist/annotations.cjs +209 -1
- package/dist/annotations.d.cts +78 -1
- package/dist/annotations.d.ts +78 -1
- package/dist/annotations.js +201 -1
- package/dist/completion.cjs +186 -50
- package/dist/completion.js +186 -50
- package/dist/constructs.cjs +310 -78
- package/dist/constructs.d.cts +525 -644
- package/dist/constructs.d.ts +525 -644
- package/dist/constructs.js +311 -79
- package/dist/context.cjs +43 -3
- package/dist/context.d.cts +113 -5
- package/dist/context.d.ts +113 -5
- package/dist/context.js +41 -3
- package/dist/dependency.cjs +172 -66
- package/dist/dependency.d.cts +22 -2
- package/dist/dependency.d.ts +22 -2
- package/dist/dependency.js +172 -66
- package/dist/doc.cjs +46 -1
- package/dist/doc.d.cts +24 -0
- package/dist/doc.d.ts +24 -0
- package/dist/doc.js +46 -1
- package/dist/facade.cjs +702 -322
- package/dist/facade.d.cts +124 -190
- package/dist/facade.d.ts +124 -190
- package/dist/facade.js +703 -323
- package/dist/index.cjs +5 -0
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/message.cjs +7 -4
- package/dist/message.js +7 -4
- package/dist/mode-dispatch.cjs +23 -1
- package/dist/mode-dispatch.d.cts +55 -0
- package/dist/mode-dispatch.d.ts +55 -0
- package/dist/mode-dispatch.js +21 -1
- package/dist/modifiers.cjs +210 -55
- package/dist/modifiers.js +211 -56
- package/dist/parser.cjs +80 -47
- package/dist/parser.d.cts +18 -3
- package/dist/parser.d.ts +18 -3
- package/dist/parser.js +82 -50
- package/dist/primitives.cjs +102 -37
- package/dist/primitives.d.cts +81 -24
- package/dist/primitives.d.ts +81 -24
- package/dist/primitives.js +103 -39
- package/dist/usage.cjs +88 -6
- package/dist/usage.d.cts +51 -13
- package/dist/usage.d.ts +51 -13
- package/dist/usage.js +85 -7
- package/dist/valueparser.cjs +371 -99
- package/dist/valueparser.d.cts +56 -7
- package/dist/valueparser.d.ts +56 -7
- package/dist/valueparser.js +371 -99
- package/package.json +10 -1
package/dist/constructs.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const require_annotations = require('./annotations.cjs');
|
|
1
2
|
const require_message = require('./message.cjs');
|
|
2
3
|
const require_dependency = require('./dependency.cjs');
|
|
3
4
|
const require_mode_dispatch = require('./mode-dispatch.cjs');
|
|
@@ -6,6 +7,7 @@ const require_suggestion = require('./suggestion.cjs');
|
|
|
6
7
|
const require_usage_internals = require('./usage-internals.cjs');
|
|
7
8
|
|
|
8
9
|
//#region src/constructs.ts
|
|
10
|
+
const inheritParentAnnotationsKey = Symbol.for("@optique/core/inheritParentAnnotations");
|
|
9
11
|
function createUnexpectedInputErrorWithScopedSuggestions(baseError, invalidInput, parsers, customFormatter) {
|
|
10
12
|
const options = /* @__PURE__ */ new Set();
|
|
11
13
|
const commands = /* @__PURE__ */ new Set();
|
|
@@ -19,6 +21,60 @@ function createUnexpectedInputErrorWithScopedSuggestions(baseError, invalidInput
|
|
|
19
21
|
...suggestionMsg
|
|
20
22
|
] : baseError;
|
|
21
23
|
}
|
|
24
|
+
function applyHiddenToUsageTerm(term, hidden) {
|
|
25
|
+
if (hidden == null) return term;
|
|
26
|
+
if (term.type === "optional") return {
|
|
27
|
+
type: "optional",
|
|
28
|
+
terms: applyHiddenToUsage(term.terms, hidden)
|
|
29
|
+
};
|
|
30
|
+
if (term.type === "multiple") return {
|
|
31
|
+
type: "multiple",
|
|
32
|
+
terms: applyHiddenToUsage(term.terms, hidden),
|
|
33
|
+
min: term.min
|
|
34
|
+
};
|
|
35
|
+
if (term.type === "exclusive") return {
|
|
36
|
+
type: "exclusive",
|
|
37
|
+
terms: term.terms.map((u) => applyHiddenToUsage(u, hidden))
|
|
38
|
+
};
|
|
39
|
+
if (term.type === "argument" || term.type === "option" || term.type === "command" || term.type === "passthrough") return {
|
|
40
|
+
...term,
|
|
41
|
+
hidden: require_usage.mergeHidden(term.hidden, hidden)
|
|
42
|
+
};
|
|
43
|
+
return term;
|
|
44
|
+
}
|
|
45
|
+
function applyHiddenToUsage(usage, hidden) {
|
|
46
|
+
if (hidden == null) return usage;
|
|
47
|
+
return usage.map((term) => applyHiddenToUsageTerm(term, hidden));
|
|
48
|
+
}
|
|
49
|
+
function applyHiddenToDocEntry(entry, hidden) {
|
|
50
|
+
const mergedTerm = applyHiddenToUsageTerm(entry.term, hidden);
|
|
51
|
+
if ((mergedTerm.type === "argument" || mergedTerm.type === "option" || mergedTerm.type === "command" || mergedTerm.type === "passthrough") && require_usage.isDocHidden(mergedTerm.hidden)) return void 0;
|
|
52
|
+
return {
|
|
53
|
+
...entry,
|
|
54
|
+
term: mergedTerm
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function applyHiddenToDocFragments(fragments, hidden) {
|
|
58
|
+
if (hidden == null) return fragments;
|
|
59
|
+
const result = [];
|
|
60
|
+
for (const fragment of fragments) {
|
|
61
|
+
if (fragment.type === "entry") {
|
|
62
|
+
const entry = applyHiddenToDocEntry(fragment, hidden);
|
|
63
|
+
if (entry != null) result.push({
|
|
64
|
+
...entry,
|
|
65
|
+
type: "entry"
|
|
66
|
+
});
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const entries = fragment.entries.map((entry) => applyHiddenToDocEntry(entry, hidden)).filter((entry) => entry != null);
|
|
70
|
+
result.push({
|
|
71
|
+
type: "section",
|
|
72
|
+
title: fragment.title,
|
|
73
|
+
entries
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
22
78
|
/**
|
|
23
79
|
* Checks if the given token is an option name that requires a value
|
|
24
80
|
* (i.e., has a metavar) within the given usage terms.
|
|
@@ -554,8 +610,9 @@ function longestMatch(...args) {
|
|
|
554
610
|
* @internal
|
|
555
611
|
*/
|
|
556
612
|
function* suggestObjectSync(context, prefix, parserPairs) {
|
|
557
|
-
const registry = context.dependencyRegistry
|
|
613
|
+
const registry = context.dependencyRegistry ? context.dependencyRegistry.clone() : new require_dependency.DependencyRegistry();
|
|
558
614
|
if (context.state && typeof context.state === "object") collectDependencies(context.state, registry);
|
|
615
|
+
completeDependencySourceDefaults(context, parserPairs, registry);
|
|
559
616
|
const contextWithRegistry = {
|
|
560
617
|
...context,
|
|
561
618
|
dependencyRegistry: registry
|
|
@@ -587,8 +644,9 @@ function* suggestObjectSync(context, prefix, parserPairs) {
|
|
|
587
644
|
* @internal
|
|
588
645
|
*/
|
|
589
646
|
async function* suggestObjectAsync(context, prefix, parserPairs) {
|
|
590
|
-
const registry = context.dependencyRegistry
|
|
647
|
+
const registry = context.dependencyRegistry ? context.dependencyRegistry.clone() : new require_dependency.DependencyRegistry();
|
|
591
648
|
if (context.state && typeof context.state === "object") collectDependencies(context.state, registry);
|
|
649
|
+
await completeDependencySourceDefaultsAsync(context, parserPairs, registry);
|
|
592
650
|
const contextWithRegistry = {
|
|
593
651
|
...context,
|
|
594
652
|
dependencyRegistry: registry
|
|
@@ -617,17 +675,62 @@ async function* suggestObjectAsync(context, prefix, parserPairs) {
|
|
|
617
675
|
yield* require_suggestion.deduplicateSuggestions(suggestions);
|
|
618
676
|
}
|
|
619
677
|
/**
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
* and re-parses DeferredParseState fields using the actual dependency values.
|
|
678
|
+
* Registers an already-resolved dependency source value in the registry
|
|
679
|
+
* if it is a successful {@link DependencySourceState}.
|
|
623
680
|
*
|
|
624
|
-
* @param fieldStates A record of field names to their state values
|
|
625
|
-
* @returns The field states with deferred states resolved to their actual values
|
|
626
681
|
* @internal
|
|
627
682
|
*/
|
|
683
|
+
function registerCompletedDependency(completed, registry) {
|
|
684
|
+
if (require_dependency.isDependencySourceState(completed) && completed.result.success && !registry.has(completed[require_dependency.dependencyId])) registry.set(completed[require_dependency.dependencyId], completed.result.value);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Yields `(parser, state)` pairs for dependency source parsers whose field
|
|
688
|
+
* state is unpopulated, so callers can invoke `complete()` on them.
|
|
689
|
+
*
|
|
690
|
+
* @see https://github.com/dahlia/optique/issues/186
|
|
691
|
+
* @internal
|
|
692
|
+
*/
|
|
693
|
+
function* pendingDependencyDefaults(context, parserPairs) {
|
|
694
|
+
for (const [field, fieldParser] of parserPairs) {
|
|
695
|
+
const fieldState = context.state != null && typeof context.state === "object" && field in context.state ? context.state[field] : void 0;
|
|
696
|
+
if (fieldState != null) continue;
|
|
697
|
+
if (require_dependency.isPendingDependencySourceState(fieldParser.initialState)) yield {
|
|
698
|
+
parser: fieldParser,
|
|
699
|
+
state: fieldParser.initialState
|
|
700
|
+
};
|
|
701
|
+
else if (require_dependency.isWrappedDependencySource(fieldParser)) yield {
|
|
702
|
+
parser: fieldParser,
|
|
703
|
+
state: [fieldParser[require_dependency.wrappedDependencySourceMarker]]
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Pre-completes dependency source parsers wrapped in `withDefault()` whose
|
|
709
|
+
* field state is unpopulated, so that their default values are registered
|
|
710
|
+
* in the dependency registry. This mirrors Phase 1 of parse-time dependency
|
|
711
|
+
* resolution and ensures `suggest()` sees the same defaults as `parse()`.
|
|
712
|
+
*
|
|
713
|
+
* @see https://github.com/dahlia/optique/issues/186
|
|
714
|
+
* @internal
|
|
715
|
+
*/
|
|
716
|
+
function completeDependencySourceDefaults(context, parserPairs, registry) {
|
|
717
|
+
for (const { parser, state } of pendingDependencyDefaults(context, parserPairs)) registerCompletedDependency(parser.complete(state), registry);
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Async version of {@link completeDependencySourceDefaults} that awaits
|
|
721
|
+
* `complete()` results. Async parsers with `transformsDependencyValue`
|
|
722
|
+
* return a `Promise` from `complete()`, which the sync version cannot handle.
|
|
723
|
+
*
|
|
724
|
+
* @see https://github.com/dahlia/optique/issues/186
|
|
725
|
+
* @internal
|
|
726
|
+
*/
|
|
727
|
+
async function completeDependencySourceDefaultsAsync(context, parserPairs, registry) {
|
|
728
|
+
for (const { parser, state } of pendingDependencyDefaults(context, parserPairs)) registerCompletedDependency(await parser.complete(state), registry);
|
|
729
|
+
}
|
|
628
730
|
/**
|
|
629
731
|
* Recursively collects dependency values from DependencySourceState objects
|
|
630
732
|
* found anywhere in the state tree.
|
|
733
|
+
* @internal
|
|
631
734
|
*/
|
|
632
735
|
function collectDependencies(state, registry, visited = /* @__PURE__ */ new WeakSet()) {
|
|
633
736
|
if (state === null || state === void 0) return;
|
|
@@ -783,8 +886,36 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
783
886
|
const parserKeys = Reflect.ownKeys(parsers);
|
|
784
887
|
const parserPairs = parserKeys.map((k) => [k, parsers[k]]);
|
|
785
888
|
parserPairs.sort(([_, parserA], [__, parserB]) => parserB.priority - parserA.priority);
|
|
786
|
-
const
|
|
787
|
-
|
|
889
|
+
const createInitialState = () => {
|
|
890
|
+
const state = {};
|
|
891
|
+
for (const key of parserKeys) state[key] = parsers[key].initialState;
|
|
892
|
+
return state;
|
|
893
|
+
};
|
|
894
|
+
const inheritedFieldStateCache = /* @__PURE__ */ new WeakMap();
|
|
895
|
+
const createFieldStateGetter = (parentState) => {
|
|
896
|
+
return (field, parser) => {
|
|
897
|
+
const fieldKey = field;
|
|
898
|
+
const cache = parentState != null && typeof parentState === "object" ? inheritedFieldStateCache.get(parentState) ?? (() => {
|
|
899
|
+
const stateCache = /* @__PURE__ */ new Map();
|
|
900
|
+
inheritedFieldStateCache.set(parentState, stateCache);
|
|
901
|
+
return stateCache;
|
|
902
|
+
})() : void 0;
|
|
903
|
+
if (cache?.has(fieldKey)) return cache.get(fieldKey);
|
|
904
|
+
const sourceState = parentState != null && typeof parentState === "object" && fieldKey in parentState ? parentState[fieldKey] : parser.initialState;
|
|
905
|
+
if (sourceState == null || typeof sourceState !== "object") {
|
|
906
|
+
cache?.set(fieldKey, sourceState);
|
|
907
|
+
return sourceState;
|
|
908
|
+
}
|
|
909
|
+
const annotations = require_annotations.getAnnotations(parentState);
|
|
910
|
+
if (annotations === void 0 || require_annotations.getAnnotations(sourceState) === annotations) {
|
|
911
|
+
cache?.set(fieldKey, sourceState);
|
|
912
|
+
return sourceState;
|
|
913
|
+
}
|
|
914
|
+
const inheritedState = Reflect.get(parser, inheritParentAnnotationsKey) === true ? require_annotations.injectAnnotations(sourceState, annotations) : require_annotations.inheritAnnotations(parentState, sourceState);
|
|
915
|
+
cache?.set(fieldKey, inheritedState);
|
|
916
|
+
return inheritedState;
|
|
917
|
+
};
|
|
918
|
+
};
|
|
788
919
|
if (!options.allowDuplicates) checkDuplicateOptionNames(parserPairs.map(([field, parser]) => [field, parser.usage]));
|
|
789
920
|
const noMatchContext = analyzeNoMatchContext(parserKeys.map((k) => parsers[k]));
|
|
790
921
|
const combinedMode = parserKeys.some((k) => parsers[k].$mode === "async") ? "async" : "sync";
|
|
@@ -809,10 +940,11 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
809
940
|
let madeProgress = true;
|
|
810
941
|
while (madeProgress && currentContext.buffer.length > 0) {
|
|
811
942
|
madeProgress = false;
|
|
943
|
+
const getFieldState = createFieldStateGetter(currentContext.state);
|
|
812
944
|
for (const [field, parser] of parserPairs) {
|
|
813
945
|
const result = parser.parse({
|
|
814
946
|
...currentContext,
|
|
815
|
-
state:
|
|
947
|
+
state: getFieldState(field, parser)
|
|
816
948
|
});
|
|
817
949
|
if (result.success && result.consumed.length > 0) {
|
|
818
950
|
currentContext = {
|
|
@@ -838,8 +970,9 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
838
970
|
};
|
|
839
971
|
if (context.buffer.length === 0) {
|
|
840
972
|
let allCanComplete = true;
|
|
973
|
+
const getFieldState = createFieldStateGetter(context.state);
|
|
841
974
|
for (const [field, parser] of parserPairs) {
|
|
842
|
-
const fieldState =
|
|
975
|
+
const fieldState = getFieldState(field, parser);
|
|
843
976
|
const completeResult = parser.complete(fieldState);
|
|
844
977
|
if (!completeResult.success) {
|
|
845
978
|
allCanComplete = false;
|
|
@@ -865,10 +998,11 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
865
998
|
let madeProgress = true;
|
|
866
999
|
while (madeProgress && currentContext.buffer.length > 0) {
|
|
867
1000
|
madeProgress = false;
|
|
1001
|
+
const getFieldState = createFieldStateGetter(currentContext.state);
|
|
868
1002
|
for (const [field, parser] of parserPairs) {
|
|
869
1003
|
const resultOrPromise = parser.parse({
|
|
870
1004
|
...currentContext,
|
|
871
|
-
state:
|
|
1005
|
+
state: getFieldState(field, parser)
|
|
872
1006
|
});
|
|
873
1007
|
const result = await resultOrPromise;
|
|
874
1008
|
if (result.success && result.consumed.length > 0) {
|
|
@@ -895,8 +1029,9 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
895
1029
|
};
|
|
896
1030
|
if (context.buffer.length === 0) {
|
|
897
1031
|
let allCanComplete = true;
|
|
1032
|
+
const getFieldState = createFieldStateGetter(context.state);
|
|
898
1033
|
for (const [field, parser] of parserPairs) {
|
|
899
|
-
const fieldState =
|
|
1034
|
+
const fieldState = getFieldState(field, parser);
|
|
900
1035
|
const completeResult = await parser.complete(fieldState);
|
|
901
1036
|
if (!completeResult.success) {
|
|
902
1037
|
allCanComplete = false;
|
|
@@ -919,8 +1054,10 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
919
1054
|
$valueType: [],
|
|
920
1055
|
$stateType: [],
|
|
921
1056
|
priority: Math.max(...parserKeys.map((k) => parsers[k].priority)),
|
|
922
|
-
usage: parserPairs.flatMap(([_, p]) => p.usage),
|
|
923
|
-
initialState
|
|
1057
|
+
usage: applyHiddenToUsage(parserPairs.flatMap(([_, p]) => p.usage), options.hidden),
|
|
1058
|
+
get initialState() {
|
|
1059
|
+
return createInitialState();
|
|
1060
|
+
},
|
|
924
1061
|
parse(context) {
|
|
925
1062
|
return require_mode_dispatch.dispatchByMode(combinedMode, () => parseSync(context), () => parseAsync(context));
|
|
926
1063
|
},
|
|
@@ -928,6 +1065,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
928
1065
|
return require_mode_dispatch.dispatchByMode(combinedMode, () => {
|
|
929
1066
|
const preCompletedState = {};
|
|
930
1067
|
const preCompletedKeys = /* @__PURE__ */ new Set();
|
|
1068
|
+
const getFieldState = createFieldStateGetter(state);
|
|
931
1069
|
for (const field of parserKeys) {
|
|
932
1070
|
const fieldKey = field;
|
|
933
1071
|
const fieldState = state[fieldKey];
|
|
@@ -947,10 +1085,11 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
947
1085
|
preCompletedState[fieldKey] = completed;
|
|
948
1086
|
preCompletedKeys.add(fieldKey);
|
|
949
1087
|
} else preCompletedState[fieldKey] = fieldState;
|
|
950
|
-
} else preCompletedState[fieldKey] =
|
|
1088
|
+
} else preCompletedState[fieldKey] = getFieldState(field, fieldParser);
|
|
951
1089
|
}
|
|
952
1090
|
const resolvedState = resolveDeferredParseStates(preCompletedState);
|
|
953
1091
|
const result = {};
|
|
1092
|
+
const getCompletionFieldState = createFieldStateGetter(state);
|
|
954
1093
|
for (const field of parserKeys) {
|
|
955
1094
|
const fieldKey = field;
|
|
956
1095
|
const fieldResolvedState = resolvedState[fieldKey];
|
|
@@ -964,7 +1103,8 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
964
1103
|
};
|
|
965
1104
|
continue;
|
|
966
1105
|
}
|
|
967
|
-
const
|
|
1106
|
+
const completionState = fieldResolvedState === void 0 ? getCompletionFieldState(field, fieldParser) : fieldResolvedState;
|
|
1107
|
+
const valueResult = fieldParser.complete(completionState);
|
|
968
1108
|
if (valueResult.success) result[fieldKey] = valueResult.value;
|
|
969
1109
|
else return {
|
|
970
1110
|
success: false,
|
|
@@ -978,6 +1118,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
978
1118
|
}, async () => {
|
|
979
1119
|
const preCompletedState = {};
|
|
980
1120
|
const preCompletedKeys = /* @__PURE__ */ new Set();
|
|
1121
|
+
const getFieldState = createFieldStateGetter(state);
|
|
981
1122
|
for (const field of parserKeys) {
|
|
982
1123
|
const fieldKey = field;
|
|
983
1124
|
const fieldState = state[fieldKey];
|
|
@@ -997,10 +1138,11 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
997
1138
|
preCompletedState[fieldKey] = completed;
|
|
998
1139
|
preCompletedKeys.add(fieldKey);
|
|
999
1140
|
} else preCompletedState[fieldKey] = fieldState;
|
|
1000
|
-
} else preCompletedState[fieldKey] =
|
|
1141
|
+
} else preCompletedState[fieldKey] = getFieldState(field, fieldParser);
|
|
1001
1142
|
}
|
|
1002
1143
|
const resolvedState = await resolveDeferredParseStatesAsync(preCompletedState);
|
|
1003
1144
|
const result = {};
|
|
1145
|
+
const getCompletionFieldState = createFieldStateGetter(state);
|
|
1004
1146
|
for (const field of parserKeys) {
|
|
1005
1147
|
const fieldKey = field;
|
|
1006
1148
|
const fieldResolvedState = resolvedState[fieldKey];
|
|
@@ -1014,7 +1156,8 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
1014
1156
|
};
|
|
1015
1157
|
continue;
|
|
1016
1158
|
}
|
|
1017
|
-
const
|
|
1159
|
+
const completionState = fieldResolvedState === void 0 ? getCompletionFieldState(field, fieldParser) : fieldResolvedState;
|
|
1160
|
+
const valueResult = await fieldParser.complete(completionState);
|
|
1018
1161
|
if (valueResult.success) result[fieldKey] = valueResult.value;
|
|
1019
1162
|
else return {
|
|
1020
1163
|
success: false,
|
|
@@ -1028,6 +1171,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
1028
1171
|
});
|
|
1029
1172
|
},
|
|
1030
1173
|
suggest(context, prefix) {
|
|
1174
|
+
if (options.hidden === true) return require_mode_dispatch.dispatchIterableByMode(combinedMode, function* () {}, async function* () {});
|
|
1031
1175
|
return require_mode_dispatch.dispatchIterableByMode(combinedMode, () => {
|
|
1032
1176
|
const syncParserPairs = parserPairs;
|
|
1033
1177
|
return suggestObjectSync(context, prefix, syncParserPairs);
|
|
@@ -1041,9 +1185,10 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
1041
1185
|
};
|
|
1042
1186
|
return p.getDocFragments(fieldState, defaultValue?.[field]).fragments;
|
|
1043
1187
|
});
|
|
1044
|
-
const
|
|
1188
|
+
const hiddenAwareFragments = applyHiddenToDocFragments(fragments, options.hidden);
|
|
1189
|
+
const entries = hiddenAwareFragments.filter((d) => d.type === "entry");
|
|
1045
1190
|
const sections = [];
|
|
1046
|
-
for (const fragment of
|
|
1191
|
+
for (const fragment of hiddenAwareFragments) {
|
|
1047
1192
|
if (fragment.type !== "section") continue;
|
|
1048
1193
|
if (fragment.title == null) entries.push(...fragment.entries);
|
|
1049
1194
|
else sections.push(fragment);
|
|
@@ -1375,9 +1520,10 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
1375
1520
|
function merge(...args) {
|
|
1376
1521
|
const label = typeof args[0] === "string" ? args[0] : void 0;
|
|
1377
1522
|
const lastArg = args[args.length - 1];
|
|
1378
|
-
const
|
|
1523
|
+
const hasOptions = lastArg != null && typeof lastArg === "object" && !("$valueType" in lastArg);
|
|
1524
|
+
const options = hasOptions ? lastArg : {};
|
|
1379
1525
|
const startIndex = typeof args[0] === "string" ? 1 : 0;
|
|
1380
|
-
const endIndex =
|
|
1526
|
+
const endIndex = hasOptions ? args.length - 1 : args.length;
|
|
1381
1527
|
const rawParsers = args.slice(startIndex, endIndex);
|
|
1382
1528
|
const combinedMode = rawParsers.some((p) => p.$mode === "async") ? "async" : "sync";
|
|
1383
1529
|
const isAsync = combinedMode === "async";
|
|
@@ -1518,7 +1664,7 @@ function merge(...args) {
|
|
|
1518
1664
|
$valueType: [],
|
|
1519
1665
|
$stateType: [],
|
|
1520
1666
|
priority: Math.max(...parsers.map((p) => p.priority)),
|
|
1521
|
-
usage: parsers.flatMap((p) => p.usage),
|
|
1667
|
+
usage: applyHiddenToUsage(parsers.flatMap((p) => p.usage), options.hidden),
|
|
1522
1668
|
initialState,
|
|
1523
1669
|
parse(context) {
|
|
1524
1670
|
if (isAsync) return parseAsync(context);
|
|
@@ -1572,6 +1718,7 @@ function merge(...args) {
|
|
|
1572
1718
|
})();
|
|
1573
1719
|
},
|
|
1574
1720
|
suggest(context, prefix) {
|
|
1721
|
+
if (options.hidden === true) return require_mode_dispatch.dispatchIterableByMode(combinedMode, function* () {}, async function* () {});
|
|
1575
1722
|
const extractState = (p, i) => {
|
|
1576
1723
|
if (p.initialState === void 0) {
|
|
1577
1724
|
const key = `__parser_${i}`;
|
|
@@ -1587,13 +1734,19 @@ function merge(...args) {
|
|
|
1587
1734
|
}
|
|
1588
1735
|
return p.initialState;
|
|
1589
1736
|
};
|
|
1737
|
+
const registry = context.dependencyRegistry ? context.dependencyRegistry.clone() : new require_dependency.DependencyRegistry();
|
|
1738
|
+
if (context.state && typeof context.state === "object") collectDependencies(context.state, registry);
|
|
1739
|
+
const contextWithRegistry = {
|
|
1740
|
+
...context,
|
|
1741
|
+
dependencyRegistry: registry
|
|
1742
|
+
};
|
|
1590
1743
|
if (isAsync) return async function* () {
|
|
1591
1744
|
const suggestions = [];
|
|
1592
1745
|
for (let i = 0; i < parsers.length; i++) {
|
|
1593
1746
|
const parser = parsers[i];
|
|
1594
1747
|
const parserState = extractState(parser, i);
|
|
1595
1748
|
const parserSuggestions = parser.suggest({
|
|
1596
|
-
...
|
|
1749
|
+
...contextWithRegistry,
|
|
1597
1750
|
state: parserState
|
|
1598
1751
|
}, prefix);
|
|
1599
1752
|
if (parser.$mode === "async") for await (const s of parserSuggestions) suggestions.push(s);
|
|
@@ -1607,7 +1760,7 @@ function merge(...args) {
|
|
|
1607
1760
|
const parser = syncParsers[i];
|
|
1608
1761
|
const parserState = extractState(parser, i);
|
|
1609
1762
|
const parserSuggestions = parser.suggest({
|
|
1610
|
-
...
|
|
1763
|
+
...contextWithRegistry,
|
|
1611
1764
|
state: parserState
|
|
1612
1765
|
}, prefix);
|
|
1613
1766
|
suggestions.push(...parserSuggestions);
|
|
@@ -1638,9 +1791,10 @@ function merge(...args) {
|
|
|
1638
1791
|
footer ??= docFragments.footer;
|
|
1639
1792
|
return docFragments.fragments;
|
|
1640
1793
|
});
|
|
1641
|
-
const
|
|
1794
|
+
const hiddenAwareFragments = applyHiddenToDocFragments(fragments, options.hidden);
|
|
1795
|
+
const entries = hiddenAwareFragments.filter((f) => f.type === "entry");
|
|
1642
1796
|
const sections = [];
|
|
1643
|
-
for (const fragment of
|
|
1797
|
+
for (const fragment of hiddenAwareFragments) {
|
|
1644
1798
|
if (fragment.type !== "section") continue;
|
|
1645
1799
|
if (fragment.title == null) entries.push(...fragment.entries);
|
|
1646
1800
|
else sections.push(fragment);
|
|
@@ -1676,6 +1830,118 @@ function merge(...args) {
|
|
|
1676
1830
|
}
|
|
1677
1831
|
};
|
|
1678
1832
|
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Builds a dependency registry from the pre-parsed context state and returns
|
|
1835
|
+
* the enriched context alongside the state array. Used by both the sync and
|
|
1836
|
+
* async branches of `concat().suggest()`.
|
|
1837
|
+
* @internal
|
|
1838
|
+
*/
|
|
1839
|
+
function buildSuggestRegistry(preParsedContext) {
|
|
1840
|
+
const stateArray = preParsedContext.state;
|
|
1841
|
+
const registry = preParsedContext.dependencyRegistry ? preParsedContext.dependencyRegistry.clone() : new require_dependency.DependencyRegistry();
|
|
1842
|
+
if (stateArray && Array.isArray(stateArray)) collectDependencies(stateArray, registry);
|
|
1843
|
+
return {
|
|
1844
|
+
context: {
|
|
1845
|
+
...preParsedContext,
|
|
1846
|
+
dependencyRegistry: registry
|
|
1847
|
+
},
|
|
1848
|
+
stateArray
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* This helper replays child parsers in priority order (mirroring
|
|
1853
|
+
* `concat.parse()`) to accumulate dependency source values for suggestions.
|
|
1854
|
+
* @internal
|
|
1855
|
+
*/
|
|
1856
|
+
function preParseSuggestContext(context, parsers) {
|
|
1857
|
+
if (context.buffer.length < 1 || !Array.isArray(context.state)) return context;
|
|
1858
|
+
return preParseSuggestLoop(context, context.state.slice(), parsers);
|
|
1859
|
+
}
|
|
1860
|
+
/**
|
|
1861
|
+
* Async variant of {@link preParseSuggestContext} that awaits async child
|
|
1862
|
+
* parsers so that dependency sources from async sub-parsers are resolved.
|
|
1863
|
+
* @internal
|
|
1864
|
+
*/
|
|
1865
|
+
async function preParseSuggestContextAsync(context, parsers) {
|
|
1866
|
+
if (context.buffer.length < 1 || !Array.isArray(context.state)) return context;
|
|
1867
|
+
return await preParseSuggestLoop(context, context.state.slice(), parsers);
|
|
1868
|
+
}
|
|
1869
|
+
/**
|
|
1870
|
+
* Shared loop for sync and async concat suggest pre-parse. When a child
|
|
1871
|
+
* parser returns a Promise its result is awaited; sync results are used
|
|
1872
|
+
* directly. Parsers are tried in priority order, matching `concat.parse()`.
|
|
1873
|
+
* @internal
|
|
1874
|
+
*/
|
|
1875
|
+
function preParseSuggestLoop(context, stateArray, parsers, matchedParsers = /* @__PURE__ */ new Set()) {
|
|
1876
|
+
const indexedParsers = parsers.map((parser, index) => [parser, index]);
|
|
1877
|
+
let currentContext = context;
|
|
1878
|
+
let changed = true;
|
|
1879
|
+
while (changed && currentContext.buffer.length > 0) {
|
|
1880
|
+
changed = false;
|
|
1881
|
+
const remaining = indexedParsers.filter(([_, index]) => !matchedParsers.has(index)).sort(([a], [b]) => b.priority - a.priority);
|
|
1882
|
+
const tried = tryParseSuggestList(currentContext, stateArray, parsers, matchedParsers, remaining);
|
|
1883
|
+
if (tried === null) continue;
|
|
1884
|
+
if (typeof tried === "object" && "then" in tried && typeof tried.then === "function") return tried;
|
|
1885
|
+
currentContext = tried;
|
|
1886
|
+
changed = true;
|
|
1887
|
+
}
|
|
1888
|
+
return {
|
|
1889
|
+
...currentContext,
|
|
1890
|
+
state: stateArray
|
|
1891
|
+
};
|
|
1892
|
+
}
|
|
1893
|
+
/**
|
|
1894
|
+
* Tries each parser in `remaining` once. Returns the updated context if a
|
|
1895
|
+
* parser consumed input (sync), a Promise that resolves to a context if an
|
|
1896
|
+
* async parser was encountered, or `null` if no parser consumed.
|
|
1897
|
+
* @internal
|
|
1898
|
+
*/
|
|
1899
|
+
function tryParseSuggestList(context, stateArray, parsers, matchedParsers, remaining) {
|
|
1900
|
+
for (let ri = 0; ri < remaining.length; ri++) {
|
|
1901
|
+
const [parser, index] = remaining[ri];
|
|
1902
|
+
const parserState = index < stateArray.length ? stateArray[index] : parser.initialState;
|
|
1903
|
+
const resultOrPromise = parser.parse({
|
|
1904
|
+
...context,
|
|
1905
|
+
state: parserState
|
|
1906
|
+
});
|
|
1907
|
+
if (resultOrPromise != null && typeof resultOrPromise === "object" && "then" in resultOrPromise && typeof resultOrPromise.then === "function") {
|
|
1908
|
+
const tail = remaining.slice(ri + 1);
|
|
1909
|
+
return resultOrPromise.then((result$1) => {
|
|
1910
|
+
if (result$1.success && result$1.consumed.length > 0) {
|
|
1911
|
+
stateArray[index] = result$1.next.state;
|
|
1912
|
+
matchedParsers.add(index);
|
|
1913
|
+
return preParseSuggestLoop({
|
|
1914
|
+
...context,
|
|
1915
|
+
buffer: result$1.next.buffer,
|
|
1916
|
+
optionsTerminated: result$1.next.optionsTerminated,
|
|
1917
|
+
state: stateArray
|
|
1918
|
+
}, stateArray, parsers, matchedParsers);
|
|
1919
|
+
}
|
|
1920
|
+
const next = tryParseSuggestList(context, stateArray, parsers, matchedParsers, tail);
|
|
1921
|
+
if (next === null) return {
|
|
1922
|
+
...context,
|
|
1923
|
+
state: stateArray
|
|
1924
|
+
};
|
|
1925
|
+
if (typeof next === "object" && "then" in next && typeof next.then === "function") return next.then((ctx) => ctx.buffer.length < context.buffer.length ? preParseSuggestLoop(ctx, stateArray, parsers, matchedParsers) : ctx);
|
|
1926
|
+
const nextCtx = next;
|
|
1927
|
+
if (nextCtx.buffer.length < context.buffer.length) return preParseSuggestLoop(nextCtx, stateArray, parsers, matchedParsers);
|
|
1928
|
+
return nextCtx;
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
const result = resultOrPromise;
|
|
1932
|
+
if (result.success && result.consumed.length > 0) {
|
|
1933
|
+
stateArray[index] = result.next.state;
|
|
1934
|
+
matchedParsers.add(index);
|
|
1935
|
+
return {
|
|
1936
|
+
...context,
|
|
1937
|
+
buffer: result.next.buffer,
|
|
1938
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
1939
|
+
state: stateArray
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
return null;
|
|
1944
|
+
}
|
|
1679
1945
|
function concat(...parsers) {
|
|
1680
1946
|
const combinedMode = parsers.some((p) => p.$mode === "async") ? "async" : "sync";
|
|
1681
1947
|
const isAsync = combinedMode === "async";
|
|
@@ -1859,14 +2125,15 @@ function concat(...parsers) {
|
|
|
1859
2125
|
return completeSync(state);
|
|
1860
2126
|
},
|
|
1861
2127
|
suggest(context, prefix) {
|
|
1862
|
-
const stateArray = context.state;
|
|
1863
2128
|
if (isAsync) return async function* () {
|
|
2129
|
+
const preParsedContext$1 = await preParseSuggestContextAsync(context, parsers);
|
|
2130
|
+
const { context: contextWithRegistry$1, stateArray: stateArray$1 } = buildSuggestRegistry(preParsedContext$1);
|
|
1864
2131
|
const suggestions = [];
|
|
1865
2132
|
for (let i = 0; i < parsers.length; i++) {
|
|
1866
2133
|
const parser = parsers[i];
|
|
1867
|
-
const parserState = stateArray && Array.isArray(stateArray) ? stateArray[i] : parser.initialState;
|
|
2134
|
+
const parserState = stateArray$1 && Array.isArray(stateArray$1) ? stateArray$1[i] : parser.initialState;
|
|
1868
2135
|
const parserSuggestions = parser.suggest({
|
|
1869
|
-
...
|
|
2136
|
+
...contextWithRegistry$1,
|
|
1870
2137
|
state: parserState
|
|
1871
2138
|
}, prefix);
|
|
1872
2139
|
if (parser.$mode === "async") for await (const s of parserSuggestions) suggestions.push(s);
|
|
@@ -1874,13 +2141,15 @@ function concat(...parsers) {
|
|
|
1874
2141
|
}
|
|
1875
2142
|
yield* require_suggestion.deduplicateSuggestions(suggestions);
|
|
1876
2143
|
}();
|
|
2144
|
+
const preParsedContext = preParseSuggestContext(context, syncParsers);
|
|
2145
|
+
const { context: contextWithRegistry, stateArray } = buildSuggestRegistry(preParsedContext);
|
|
1877
2146
|
return function* () {
|
|
1878
2147
|
const suggestions = [];
|
|
1879
2148
|
for (let i = 0; i < syncParsers.length; i++) {
|
|
1880
2149
|
const parser = syncParsers[i];
|
|
1881
2150
|
const parserState = stateArray && Array.isArray(stateArray) ? stateArray[i] : parser.initialState;
|
|
1882
2151
|
const parserSuggestions = parser.suggest({
|
|
1883
|
-
...
|
|
2152
|
+
...contextWithRegistry,
|
|
1884
2153
|
state: parserState
|
|
1885
2154
|
}, prefix);
|
|
1886
2155
|
suggestions.push(...parserSuggestions);
|
|
@@ -1915,64 +2184,27 @@ function concat(...parsers) {
|
|
|
1915
2184
|
}
|
|
1916
2185
|
};
|
|
1917
2186
|
}
|
|
1918
|
-
|
|
1919
|
-
* Wraps a parser with a group label for documentation purposes.
|
|
1920
|
-
*
|
|
1921
|
-
* The `group()` function is a documentation-only wrapper that applies a label
|
|
1922
|
-
* to any parser for help text organization. This allows you to use clean code
|
|
1923
|
-
* structure with combinators like {@link merge} while maintaining well-organized
|
|
1924
|
-
* help text through group labeling.
|
|
1925
|
-
*
|
|
1926
|
-
* The wrapped parser has identical parsing behavior but generates documentation
|
|
1927
|
-
* fragments wrapped in a labeled section. This is particularly useful when
|
|
1928
|
-
* combining parsers using {@link merge}—you can wrap the merged result with
|
|
1929
|
-
* `group()` to add a section header in help output.
|
|
1930
|
-
*
|
|
1931
|
-
* @example
|
|
1932
|
-
* ```typescript
|
|
1933
|
-
* const apiOptions = merge(
|
|
1934
|
-
* object({ endpoint: option("--endpoint", string()) }),
|
|
1935
|
-
* object({ timeout: option("--timeout", integer()) })
|
|
1936
|
-
* );
|
|
1937
|
-
*
|
|
1938
|
-
* const groupedApiOptions = group("API Options", apiOptions);
|
|
1939
|
-
* // Now produces a labeled "API Options" section in help text
|
|
1940
|
-
* ```
|
|
1941
|
-
*
|
|
1942
|
-
* @example
|
|
1943
|
-
* ```typescript
|
|
1944
|
-
* // Can be used with any parser, not just merge()
|
|
1945
|
-
* const verboseGroup = group("Verbosity", object({
|
|
1946
|
-
* verbose: option("-v", "--verbose"),
|
|
1947
|
-
* quiet: option("-q", "--quiet")
|
|
1948
|
-
* }));
|
|
1949
|
-
* ```
|
|
1950
|
-
*
|
|
1951
|
-
* @template TValue The value type of the wrapped parser.
|
|
1952
|
-
* @template TState The state type of the wrapped parser.
|
|
1953
|
-
* @param label A descriptive label for this parser group, used for
|
|
1954
|
-
* documentation and help text organization.
|
|
1955
|
-
* @param parser The parser to wrap with a group label.
|
|
1956
|
-
* @returns A new parser that behaves identically to the input parser
|
|
1957
|
-
* but generates documentation within a labeled section.
|
|
1958
|
-
* @since 0.4.0
|
|
1959
|
-
*/
|
|
1960
|
-
function group(label, parser) {
|
|
2187
|
+
function group(label, parser, options = {}) {
|
|
1961
2188
|
return {
|
|
1962
2189
|
$mode: parser.$mode,
|
|
1963
2190
|
$valueType: parser.$valueType,
|
|
1964
2191
|
$stateType: parser.$stateType,
|
|
1965
2192
|
priority: parser.priority,
|
|
1966
|
-
usage: parser.usage,
|
|
2193
|
+
usage: applyHiddenToUsage(parser.usage, options.hidden),
|
|
1967
2194
|
initialState: parser.initialState,
|
|
2195
|
+
...typeof parser.shouldDeferCompletion === "function" ? { shouldDeferCompletion: parser.shouldDeferCompletion.bind(parser) } : {},
|
|
1968
2196
|
parse: (context) => parser.parse(context),
|
|
1969
2197
|
complete: (state) => parser.complete(state),
|
|
1970
|
-
suggest: (context, prefix) =>
|
|
2198
|
+
suggest: (context, prefix) => {
|
|
2199
|
+
if (options.hidden === true) return require_mode_dispatch.dispatchIterableByMode(parser.$mode, function* () {}, async function* () {});
|
|
2200
|
+
return parser.suggest(context, prefix);
|
|
2201
|
+
},
|
|
1971
2202
|
getDocFragments: (state, defaultValue) => {
|
|
1972
2203
|
const { brief, description, footer, fragments } = parser.getDocFragments(state, defaultValue);
|
|
2204
|
+
const hiddenAwareFragments = applyHiddenToDocFragments(fragments, options.hidden);
|
|
1973
2205
|
const allEntries = [];
|
|
1974
2206
|
const titledSections = [];
|
|
1975
|
-
for (const fragment of
|
|
2207
|
+
for (const fragment of hiddenAwareFragments) if (fragment.type === "entry") allEntries.push(fragment);
|
|
1976
2208
|
else if (fragment.type === "section") if (fragment.title) titledSections.push(fragment);
|
|
1977
2209
|
else allEntries.push(...fragment.entries);
|
|
1978
2210
|
const initialFragments = parser.getDocFragments({
|