@optique/core 1.0.0-dev.1536 → 1.0.0-dev.1553
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 +113 -5
- package/dist/constructs.js +113 -5
- package/dist/dependency.cjs +34 -0
- package/dist/dependency.js +34 -0
- package/dist/facade.cjs +5 -3
- package/dist/facade.js +6 -4
- package/dist/index.cjs +0 -3
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/modifiers.cjs +81 -9
- package/dist/modifiers.js +81 -9
- package/dist/parser.d.cts +58 -0
- package/dist/parser.d.ts +58 -0
- package/dist/primitives.cjs +52 -0
- package/dist/primitives.js +52 -0
- package/dist/usage.cjs +0 -194
- package/dist/usage.d.cts +1 -75
- package/dist/usage.d.ts +1 -75
- package/dist/usage.js +1 -192
- package/dist/validate.cjs +24 -13
- package/dist/validate.js +24 -13
- package/dist/valueparser.cjs +246 -30
- package/dist/valueparser.d.cts +27 -0
- package/dist/valueparser.d.ts +27 -0
- package/dist/valueparser.js +246 -30
- package/package.json +1 -1
package/dist/parser.d.ts
CHANGED
|
@@ -105,6 +105,44 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
|
|
|
105
105
|
* to use it in command-line interfaces.
|
|
106
106
|
*/
|
|
107
107
|
readonly usage: Usage;
|
|
108
|
+
/**
|
|
109
|
+
* Names that this parser could match at the first buffer position.
|
|
110
|
+
* Used by `runParser()` to detect collisions with built-in meta
|
|
111
|
+
* features (help, version, completion).
|
|
112
|
+
*
|
|
113
|
+
* Each built-in combinator computes this from its structural semantics.
|
|
114
|
+
* Custom parser implementations must include every fixed token that
|
|
115
|
+
* the parser accepts at `argv[0]` — command names, option names, and
|
|
116
|
+
* literal values alike. For example, a parser whose usage declares
|
|
117
|
+
* `{ type: "literal", value: "serve" }` should include `"serve"` in
|
|
118
|
+
* this set. Parsers that accept *any* token (like `argument()`) should
|
|
119
|
+
* return an empty set and set {@link acceptingAnyToken} to `true`
|
|
120
|
+
* instead.
|
|
121
|
+
*
|
|
122
|
+
* @since 1.0.0
|
|
123
|
+
*/
|
|
124
|
+
readonly leadingNames: ReadonlySet<string>;
|
|
125
|
+
/**
|
|
126
|
+
* Whether this parser unconditionally consumes any positional token at
|
|
127
|
+
* the first buffer position. A parser with this flag accepts any
|
|
128
|
+
* non-option token but may still reject option-like tokens (those
|
|
129
|
+
* starting with `"-"`).
|
|
130
|
+
*
|
|
131
|
+
* In shared-buffer compositions (`tuple()`, `object()`, `merge()`,
|
|
132
|
+
* `concat()`), a catch-all parser blocks positional names (command
|
|
133
|
+
* names) from lower-priority siblings but does not block option-like
|
|
134
|
+
* names. In `conditional()`, option-like names from the default
|
|
135
|
+
* branch remain reachable even when the discriminator is a catch-all.
|
|
136
|
+
*
|
|
137
|
+
* Only `argument()` is inherently accepting-any-token; combinators
|
|
138
|
+
* like `or()` and `map()` propagate this from their children.
|
|
139
|
+
* Wrappers that can succeed without consuming (`optional()`,
|
|
140
|
+
* `withDefault()`, `multiple()` with `min = 0`) always set this
|
|
141
|
+
* to `false`.
|
|
142
|
+
*
|
|
143
|
+
* @since 1.0.0
|
|
144
|
+
*/
|
|
145
|
+
readonly acceptingAnyToken: boolean;
|
|
108
146
|
/**
|
|
109
147
|
* The initial state for this parser. This is used to initialize the
|
|
110
148
|
* state when parsing starts.
|
|
@@ -187,6 +225,26 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
|
|
|
187
225
|
* @since 1.0.0
|
|
188
226
|
*/
|
|
189
227
|
shouldDeferCompletion?(state: TState): boolean;
|
|
228
|
+
/**
|
|
229
|
+
* Normalizes a parsed value according to the underlying value parser's
|
|
230
|
+
* configuration. When present, {@link withDefault} calls this method
|
|
231
|
+
* on default values so that runtime defaults match the representation
|
|
232
|
+
* that the value parser's `parse()` would produce.
|
|
233
|
+
*
|
|
234
|
+
* Primitive parsers ({@link option}, {@link argument}) implement this
|
|
235
|
+
* by delegating to {@link ValueParser.normalize}. Combinator wrappers
|
|
236
|
+
* ({@link optional}, {@link withDefault}) forward it from inner parsers.
|
|
237
|
+
*
|
|
238
|
+
* Exclusive combinators ({@link or}, `longestMatch()`) and
|
|
239
|
+
* multi-source combinators (`merge()`) intentionally do *not*
|
|
240
|
+
* implement this method because the active branch or key ownership
|
|
241
|
+
* is unknown at default time.
|
|
242
|
+
*
|
|
243
|
+
* @param value The value to normalize.
|
|
244
|
+
* @returns The normalized value.
|
|
245
|
+
* @since 1.0.0
|
|
246
|
+
*/
|
|
247
|
+
normalizeValue?(value: TValue): TValue;
|
|
190
248
|
}
|
|
191
249
|
/**
|
|
192
250
|
* The context of the parser, which includes the input buffer and the state.
|
package/dist/primitives.cjs
CHANGED
|
@@ -9,6 +9,11 @@ const require_usage_internals = require('./usage-internals.cjs');
|
|
|
9
9
|
const require_valueparser = require('./valueparser.cjs');
|
|
10
10
|
|
|
11
11
|
//#region src/primitives.ts
|
|
12
|
+
/**
|
|
13
|
+
* A shared empty set used as the `leadingNames` value for parsers that
|
|
14
|
+
* do not match any specific name at the first buffer position.
|
|
15
|
+
*/
|
|
16
|
+
const EMPTY_LEADING_NAMES = /* @__PURE__ */ new Set();
|
|
12
17
|
function hasParsedOptionValue(state, valueParser) {
|
|
13
18
|
if (valueParser != null) return require_dependency.isDeferredParseState(state) || require_dependency.isDependencySourceState(state) || state != null && "success" in state && state.success;
|
|
14
19
|
return state != null && "success" in state && state.success && state.value === true;
|
|
@@ -38,6 +43,8 @@ function constant(value) {
|
|
|
38
43
|
$mode: "sync",
|
|
39
44
|
priority: 0,
|
|
40
45
|
usage: [],
|
|
46
|
+
leadingNames: EMPTY_LEADING_NAMES,
|
|
47
|
+
acceptingAnyToken: false,
|
|
41
48
|
initialState: value,
|
|
42
49
|
parse(context) {
|
|
43
50
|
return {
|
|
@@ -93,6 +100,8 @@ function fail() {
|
|
|
93
100
|
$mode: "sync",
|
|
94
101
|
priority: 0,
|
|
95
102
|
usage: [],
|
|
103
|
+
leadingNames: EMPTY_LEADING_NAMES,
|
|
104
|
+
acceptingAnyToken: false,
|
|
96
105
|
initialState: void 0,
|
|
97
106
|
parse(_context) {
|
|
98
107
|
return {
|
|
@@ -338,6 +347,8 @@ function option(...args) {
|
|
|
338
347
|
metavar: valueParser.metavar,
|
|
339
348
|
...options.hidden != null && { hidden: options.hidden }
|
|
340
349
|
}],
|
|
350
|
+
leadingNames: new Set(optionNames$1),
|
|
351
|
+
acceptingAnyToken: false,
|
|
341
352
|
initialState: valueParser == null ? {
|
|
342
353
|
success: true,
|
|
343
354
|
value: false
|
|
@@ -553,6 +564,20 @@ function option(...args) {
|
|
|
553
564
|
return `option(${optionNames$1.map((o) => JSON.stringify(o)).join(", ")})`;
|
|
554
565
|
}
|
|
555
566
|
};
|
|
567
|
+
if (valueParser != null && typeof valueParser.normalize === "function") {
|
|
568
|
+
const normalize = valueParser.normalize.bind(valueParser);
|
|
569
|
+
Object.defineProperty(result, "normalizeValue", {
|
|
570
|
+
value(v) {
|
|
571
|
+
try {
|
|
572
|
+
return normalize(v);
|
|
573
|
+
} catch {
|
|
574
|
+
return v;
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
configurable: true,
|
|
578
|
+
enumerable: false
|
|
579
|
+
});
|
|
580
|
+
}
|
|
556
581
|
if (valueParser != null) Object.defineProperty(result, "placeholder", {
|
|
557
582
|
get() {
|
|
558
583
|
try {
|
|
@@ -622,6 +647,8 @@ function flag(...args) {
|
|
|
622
647
|
names: optionNames$1,
|
|
623
648
|
...options.hidden != null && { hidden: options.hidden }
|
|
624
649
|
}],
|
|
650
|
+
leadingNames: new Set(optionNames$1),
|
|
651
|
+
acceptingAnyToken: false,
|
|
625
652
|
initialState: void 0,
|
|
626
653
|
parse(context) {
|
|
627
654
|
if (context.optionsTerminated) return {
|
|
@@ -797,6 +824,8 @@ function argument(valueParser, options = {}) {
|
|
|
797
824
|
$stateType: [],
|
|
798
825
|
priority: 5,
|
|
799
826
|
usage: [term],
|
|
827
|
+
leadingNames: EMPTY_LEADING_NAMES,
|
|
828
|
+
acceptingAnyToken: true,
|
|
800
829
|
initialState: void 0,
|
|
801
830
|
parse(context) {
|
|
802
831
|
if (context.buffer.length < 1) return {
|
|
@@ -903,6 +932,20 @@ function argument(valueParser, options = {}) {
|
|
|
903
932
|
return `argument()`;
|
|
904
933
|
}
|
|
905
934
|
};
|
|
935
|
+
if (typeof valueParser.normalize === "function") {
|
|
936
|
+
const normalize = valueParser.normalize.bind(valueParser);
|
|
937
|
+
Object.defineProperty(result, "normalizeValue", {
|
|
938
|
+
value(v) {
|
|
939
|
+
try {
|
|
940
|
+
return normalize(v);
|
|
941
|
+
} catch {
|
|
942
|
+
return v;
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
configurable: true,
|
|
946
|
+
enumerable: false
|
|
947
|
+
});
|
|
948
|
+
}
|
|
906
949
|
Object.defineProperty(result, "placeholder", {
|
|
907
950
|
get() {
|
|
908
951
|
try {
|
|
@@ -986,6 +1029,8 @@ function command(name, parser, options = {}) {
|
|
|
986
1029
|
...options.usageLine != null && { usageLine: options.usageLine },
|
|
987
1030
|
...options.hidden != null && { hidden: options.hidden }
|
|
988
1031
|
}, ...parser.usage],
|
|
1032
|
+
leadingNames: new Set([name]),
|
|
1033
|
+
acceptingAnyToken: false,
|
|
989
1034
|
initialState: void 0,
|
|
990
1035
|
parse(context) {
|
|
991
1036
|
if (context.state === void 0) {
|
|
@@ -1119,6 +1164,11 @@ function command(name, parser, options = {}) {
|
|
|
1119
1164
|
return `command(${JSON.stringify(name)})`;
|
|
1120
1165
|
}
|
|
1121
1166
|
};
|
|
1167
|
+
if (typeof parser.normalizeValue === "function") Object.defineProperty(result, "normalizeValue", {
|
|
1168
|
+
value: parser.normalizeValue.bind(parser),
|
|
1169
|
+
configurable: true,
|
|
1170
|
+
enumerable: false
|
|
1171
|
+
});
|
|
1122
1172
|
return result;
|
|
1123
1173
|
}
|
|
1124
1174
|
/**
|
|
@@ -1180,6 +1230,8 @@ function passThrough(options = {}) {
|
|
|
1180
1230
|
type: "passthrough",
|
|
1181
1231
|
...options.hidden != null && { hidden: options.hidden }
|
|
1182
1232
|
}],
|
|
1233
|
+
leadingNames: EMPTY_LEADING_NAMES,
|
|
1234
|
+
acceptingAnyToken: false,
|
|
1183
1235
|
initialState: [],
|
|
1184
1236
|
parse(context) {
|
|
1185
1237
|
if (context.buffer.length < 1) return {
|
package/dist/primitives.js
CHANGED
|
@@ -9,6 +9,11 @@ import { extractLeadingCommandNames } from "./usage-internals.js";
|
|
|
9
9
|
import { isValueParser } from "./valueparser.js";
|
|
10
10
|
|
|
11
11
|
//#region src/primitives.ts
|
|
12
|
+
/**
|
|
13
|
+
* A shared empty set used as the `leadingNames` value for parsers that
|
|
14
|
+
* do not match any specific name at the first buffer position.
|
|
15
|
+
*/
|
|
16
|
+
const EMPTY_LEADING_NAMES = /* @__PURE__ */ new Set();
|
|
12
17
|
function hasParsedOptionValue(state, valueParser) {
|
|
13
18
|
if (valueParser != null) return isDeferredParseState(state) || isDependencySourceState(state) || state != null && "success" in state && state.success;
|
|
14
19
|
return state != null && "success" in state && state.success && state.value === true;
|
|
@@ -38,6 +43,8 @@ function constant(value) {
|
|
|
38
43
|
$mode: "sync",
|
|
39
44
|
priority: 0,
|
|
40
45
|
usage: [],
|
|
46
|
+
leadingNames: EMPTY_LEADING_NAMES,
|
|
47
|
+
acceptingAnyToken: false,
|
|
41
48
|
initialState: value,
|
|
42
49
|
parse(context) {
|
|
43
50
|
return {
|
|
@@ -93,6 +100,8 @@ function fail() {
|
|
|
93
100
|
$mode: "sync",
|
|
94
101
|
priority: 0,
|
|
95
102
|
usage: [],
|
|
103
|
+
leadingNames: EMPTY_LEADING_NAMES,
|
|
104
|
+
acceptingAnyToken: false,
|
|
96
105
|
initialState: void 0,
|
|
97
106
|
parse(_context) {
|
|
98
107
|
return {
|
|
@@ -338,6 +347,8 @@ function option(...args) {
|
|
|
338
347
|
metavar: valueParser.metavar,
|
|
339
348
|
...options.hidden != null && { hidden: options.hidden }
|
|
340
349
|
}],
|
|
350
|
+
leadingNames: new Set(optionNames$1),
|
|
351
|
+
acceptingAnyToken: false,
|
|
341
352
|
initialState: valueParser == null ? {
|
|
342
353
|
success: true,
|
|
343
354
|
value: false
|
|
@@ -553,6 +564,20 @@ function option(...args) {
|
|
|
553
564
|
return `option(${optionNames$1.map((o) => JSON.stringify(o)).join(", ")})`;
|
|
554
565
|
}
|
|
555
566
|
};
|
|
567
|
+
if (valueParser != null && typeof valueParser.normalize === "function") {
|
|
568
|
+
const normalize = valueParser.normalize.bind(valueParser);
|
|
569
|
+
Object.defineProperty(result, "normalizeValue", {
|
|
570
|
+
value(v) {
|
|
571
|
+
try {
|
|
572
|
+
return normalize(v);
|
|
573
|
+
} catch {
|
|
574
|
+
return v;
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
configurable: true,
|
|
578
|
+
enumerable: false
|
|
579
|
+
});
|
|
580
|
+
}
|
|
556
581
|
if (valueParser != null) Object.defineProperty(result, "placeholder", {
|
|
557
582
|
get() {
|
|
558
583
|
try {
|
|
@@ -622,6 +647,8 @@ function flag(...args) {
|
|
|
622
647
|
names: optionNames$1,
|
|
623
648
|
...options.hidden != null && { hidden: options.hidden }
|
|
624
649
|
}],
|
|
650
|
+
leadingNames: new Set(optionNames$1),
|
|
651
|
+
acceptingAnyToken: false,
|
|
625
652
|
initialState: void 0,
|
|
626
653
|
parse(context) {
|
|
627
654
|
if (context.optionsTerminated) return {
|
|
@@ -797,6 +824,8 @@ function argument(valueParser, options = {}) {
|
|
|
797
824
|
$stateType: [],
|
|
798
825
|
priority: 5,
|
|
799
826
|
usage: [term],
|
|
827
|
+
leadingNames: EMPTY_LEADING_NAMES,
|
|
828
|
+
acceptingAnyToken: true,
|
|
800
829
|
initialState: void 0,
|
|
801
830
|
parse(context) {
|
|
802
831
|
if (context.buffer.length < 1) return {
|
|
@@ -903,6 +932,20 @@ function argument(valueParser, options = {}) {
|
|
|
903
932
|
return `argument()`;
|
|
904
933
|
}
|
|
905
934
|
};
|
|
935
|
+
if (typeof valueParser.normalize === "function") {
|
|
936
|
+
const normalize = valueParser.normalize.bind(valueParser);
|
|
937
|
+
Object.defineProperty(result, "normalizeValue", {
|
|
938
|
+
value(v) {
|
|
939
|
+
try {
|
|
940
|
+
return normalize(v);
|
|
941
|
+
} catch {
|
|
942
|
+
return v;
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
configurable: true,
|
|
946
|
+
enumerable: false
|
|
947
|
+
});
|
|
948
|
+
}
|
|
906
949
|
Object.defineProperty(result, "placeholder", {
|
|
907
950
|
get() {
|
|
908
951
|
try {
|
|
@@ -986,6 +1029,8 @@ function command(name, parser, options = {}) {
|
|
|
986
1029
|
...options.usageLine != null && { usageLine: options.usageLine },
|
|
987
1030
|
...options.hidden != null && { hidden: options.hidden }
|
|
988
1031
|
}, ...parser.usage],
|
|
1032
|
+
leadingNames: new Set([name]),
|
|
1033
|
+
acceptingAnyToken: false,
|
|
989
1034
|
initialState: void 0,
|
|
990
1035
|
parse(context) {
|
|
991
1036
|
if (context.state === void 0) {
|
|
@@ -1119,6 +1164,11 @@ function command(name, parser, options = {}) {
|
|
|
1119
1164
|
return `command(${JSON.stringify(name)})`;
|
|
1120
1165
|
}
|
|
1121
1166
|
};
|
|
1167
|
+
if (typeof parser.normalizeValue === "function") Object.defineProperty(result, "normalizeValue", {
|
|
1168
|
+
value: parser.normalizeValue.bind(parser),
|
|
1169
|
+
configurable: true,
|
|
1170
|
+
enumerable: false
|
|
1171
|
+
});
|
|
1122
1172
|
return result;
|
|
1123
1173
|
}
|
|
1124
1174
|
/**
|
|
@@ -1180,6 +1230,8 @@ function passThrough(options = {}) {
|
|
|
1180
1230
|
type: "passthrough",
|
|
1181
1231
|
...options.hidden != null && { hidden: options.hidden }
|
|
1182
1232
|
}],
|
|
1233
|
+
leadingNames: EMPTY_LEADING_NAMES,
|
|
1234
|
+
acceptingAnyToken: false,
|
|
1183
1235
|
initialState: [],
|
|
1184
1236
|
parse(context) {
|
|
1185
1237
|
if (context.buffer.length < 1) return {
|
package/dist/usage.cjs
CHANGED
|
@@ -104,197 +104,6 @@ function extractCommandNames(usage, includeHidden) {
|
|
|
104
104
|
return names;
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
|
-
* Extracts option names that are reachable at the leading token position
|
|
108
|
-
* (before any command or argument gate).
|
|
109
|
-
*
|
|
110
|
-
* Unlike {@link extractOptionNames}, which traverses the entire usage tree,
|
|
111
|
-
* this function stops scanning a terms array after encountering a `command`,
|
|
112
|
-
* `argument`, or `literal` term, because subsequent terms are scoped under
|
|
113
|
-
* that positional token. It still recurses into `optional`, `multiple`,
|
|
114
|
-
* and `exclusive` containers, since they represent alternatives or wrappers
|
|
115
|
-
* at the same position.
|
|
116
|
-
*
|
|
117
|
-
* Known limitation: this function infers token positions from the `usage`
|
|
118
|
-
* tree, which is a display-oriented structure. Combinators like `tuple()`
|
|
119
|
-
* and `object()` sort or flatten usage by priority rather than token order,
|
|
120
|
-
* so the results can be inaccurate—both false positives (e.g., options
|
|
121
|
-
* appearing before commands due to priority sorting) and false negatives
|
|
122
|
-
* (e.g., options after commands that are actually parallel peers).
|
|
123
|
-
* The proper fix is to use `Parser.leadingNames` instead of usage-tree
|
|
124
|
-
* analysis.
|
|
125
|
-
* See https://github.com/dahlia/optique/issues/735
|
|
126
|
-
*
|
|
127
|
-
* @param usage The usage description to extract leading option names from.
|
|
128
|
-
* @param includeHidden Whether to include fully hidden options
|
|
129
|
-
* (`hidden: true`) in the result. Defaults to `false`.
|
|
130
|
-
* @returns A set of option names reachable at the leading token position.
|
|
131
|
-
* @since 1.0.0
|
|
132
|
-
*/
|
|
133
|
-
function extractLeadingOptionNames(usage, includeHidden) {
|
|
134
|
-
const names = /* @__PURE__ */ new Set();
|
|
135
|
-
function collectLeading(terms) {
|
|
136
|
-
if (!terms || !Array.isArray(terms)) return;
|
|
137
|
-
for (const term of terms) switch (term.type) {
|
|
138
|
-
case "option":
|
|
139
|
-
if (!includeHidden && isSuggestionHidden(term.hidden)) break;
|
|
140
|
-
for (const name of term.names) names.add(name);
|
|
141
|
-
break;
|
|
142
|
-
case "command": return;
|
|
143
|
-
case "argument":
|
|
144
|
-
case "literal": return;
|
|
145
|
-
case "optional":
|
|
146
|
-
collectLeading(term.terms);
|
|
147
|
-
break;
|
|
148
|
-
case "multiple":
|
|
149
|
-
collectLeading(term.terms);
|
|
150
|
-
if (term.min > 0 && branchConsumesToken(term.terms)) return;
|
|
151
|
-
break;
|
|
152
|
-
case "exclusive":
|
|
153
|
-
for (const branch of term.terms) collectLeading(branch);
|
|
154
|
-
if (exclusiveConsumesToken(term.terms)) return;
|
|
155
|
-
break;
|
|
156
|
-
default: break;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
collectLeading(usage);
|
|
160
|
-
return names;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Extracts command names that could match as the first positional token.
|
|
164
|
-
*
|
|
165
|
-
* Unlike {@link extractCommandNames}, which traverses the entire usage tree,
|
|
166
|
-
* this function stops scanning a terms array after encountering a `command`,
|
|
167
|
-
* `argument`, or `literal` term, because subsequent terms in that array are
|
|
168
|
-
* scoped under that positional token (reachable only after the leading term
|
|
169
|
-
* is consumed). It still recurses into `optional`, `multiple`, and
|
|
170
|
-
* `exclusive` containers, since they represent alternatives or optional
|
|
171
|
-
* wrappers at the same token position.
|
|
172
|
-
*
|
|
173
|
-
* Known limitation: this function has the same usage-tree ordering caveat
|
|
174
|
-
* as {@link extractLeadingOptionNames}.
|
|
175
|
-
* See https://github.com/dahlia/optique/issues/735
|
|
176
|
-
*
|
|
177
|
-
* @param usage The usage description to extract leading command names from.
|
|
178
|
-
* @param includeHidden Whether to include fully hidden commands
|
|
179
|
-
* (`hidden: true`) in the result. Defaults to `false`.
|
|
180
|
-
* @returns A set of command names that could match at the first token position.
|
|
181
|
-
* @since 1.0.0
|
|
182
|
-
*/
|
|
183
|
-
function extractLeadingCommandNames(usage, includeHidden) {
|
|
184
|
-
const names = /* @__PURE__ */ new Set();
|
|
185
|
-
function collectLeading(terms) {
|
|
186
|
-
if (!terms || !Array.isArray(terms)) return;
|
|
187
|
-
for (const term of terms) switch (term.type) {
|
|
188
|
-
case "command":
|
|
189
|
-
if (!includeHidden && isSuggestionHidden(term.hidden)) return;
|
|
190
|
-
names.add(term.name);
|
|
191
|
-
return;
|
|
192
|
-
case "argument":
|
|
193
|
-
case "literal": return;
|
|
194
|
-
case "optional":
|
|
195
|
-
collectLeading(term.terms);
|
|
196
|
-
break;
|
|
197
|
-
case "multiple":
|
|
198
|
-
collectLeading(term.terms);
|
|
199
|
-
if (term.min > 0 && branchConsumesToken(term.terms)) return;
|
|
200
|
-
break;
|
|
201
|
-
case "exclusive":
|
|
202
|
-
for (const branch of term.terms) collectLeading(branch);
|
|
203
|
-
if (exclusiveConsumesToken(term.terms)) return;
|
|
204
|
-
break;
|
|
205
|
-
default: break;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
collectLeading(usage);
|
|
209
|
-
return names;
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Extracts literal values that could match as the first positional token.
|
|
213
|
-
*
|
|
214
|
-
* Unlike {@link extractLiteralValues}, which traverses the entire usage tree,
|
|
215
|
-
* this function stops scanning a terms array after encountering a `command`,
|
|
216
|
-
* `argument`, or `literal` term, because subsequent terms in that array are
|
|
217
|
-
* scoped under that positional token. It still recurses into `optional`,
|
|
218
|
-
* `multiple`, and `exclusive` containers.
|
|
219
|
-
*
|
|
220
|
-
* Literals tagged with `optionValue: true` (produced by
|
|
221
|
-
* `appendLiteralToUsage()` when rewriting option metavars for
|
|
222
|
-
* `conditional()` discriminators) are skipped, because they represent
|
|
223
|
-
* option values rather than standalone positional tokens.
|
|
224
|
-
*
|
|
225
|
-
* Known limitation: this function has the same usage-tree ordering caveat
|
|
226
|
-
* as {@link extractLeadingOptionNames}.
|
|
227
|
-
* See https://github.com/dahlia/optique/issues/735
|
|
228
|
-
*
|
|
229
|
-
* @param usage The usage description to extract leading literal values from.
|
|
230
|
-
* @returns A set of literal values that could match at the first token
|
|
231
|
-
* position.
|
|
232
|
-
* @since 1.0.0
|
|
233
|
-
*/
|
|
234
|
-
function extractLeadingLiteralValues(usage) {
|
|
235
|
-
const values = /* @__PURE__ */ new Set();
|
|
236
|
-
function collectLeading(terms) {
|
|
237
|
-
if (!terms || !Array.isArray(terms)) return;
|
|
238
|
-
for (const term of terms) switch (term.type) {
|
|
239
|
-
case "literal":
|
|
240
|
-
if (term.optionValue) break;
|
|
241
|
-
values.add(term.value);
|
|
242
|
-
return;
|
|
243
|
-
case "command":
|
|
244
|
-
case "argument": return;
|
|
245
|
-
case "optional":
|
|
246
|
-
collectLeading(term.terms);
|
|
247
|
-
break;
|
|
248
|
-
case "multiple":
|
|
249
|
-
collectLeading(term.terms);
|
|
250
|
-
if (term.min > 0 && branchConsumesToken(term.terms, true)) return;
|
|
251
|
-
break;
|
|
252
|
-
case "exclusive":
|
|
253
|
-
for (const branch of term.terms) collectLeading(branch);
|
|
254
|
-
if (exclusiveConsumesToken(term.terms, true)) return;
|
|
255
|
-
break;
|
|
256
|
-
default: break;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
collectLeading(usage);
|
|
260
|
-
return values;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Checks whether every branch of an exclusive term must consume a positional
|
|
264
|
-
* token. When true, terms after the exclusive are at position N+1 and should
|
|
265
|
-
* not be considered "leading".
|
|
266
|
-
*
|
|
267
|
-
* @param skipOptionValueLiterals When `true`, literals tagged with
|
|
268
|
-
* `optionValue` are treated as non-positional. Only
|
|
269
|
-
* `extractLeadingLiteralValues()` passes `true`; the option/command
|
|
270
|
-
* extractors use the default (`false`) so that option+value pairs
|
|
271
|
-
* still act as positional gates.
|
|
272
|
-
*/
|
|
273
|
-
function exclusiveConsumesToken(branches, skipOptionValueLiterals = false) {
|
|
274
|
-
if (branches.length === 0) return false;
|
|
275
|
-
return branches.every((branch) => branchConsumesToken(branch, skipOptionValueLiterals));
|
|
276
|
-
}
|
|
277
|
-
function branchConsumesToken(terms, skipOptionValueLiterals = false) {
|
|
278
|
-
if (!terms || !Array.isArray(terms)) return false;
|
|
279
|
-
for (const term of terms) switch (term.type) {
|
|
280
|
-
case "command":
|
|
281
|
-
case "argument": return true;
|
|
282
|
-
case "literal":
|
|
283
|
-
if (skipOptionValueLiterals && term.optionValue) break;
|
|
284
|
-
return true;
|
|
285
|
-
case "option": break;
|
|
286
|
-
case "optional": break;
|
|
287
|
-
case "multiple":
|
|
288
|
-
if (term.min > 0 && branchConsumesToken(term.terms, skipOptionValueLiterals)) return true;
|
|
289
|
-
break;
|
|
290
|
-
case "exclusive":
|
|
291
|
-
if (exclusiveConsumesToken(term.terms, skipOptionValueLiterals)) return true;
|
|
292
|
-
break;
|
|
293
|
-
default: break;
|
|
294
|
-
}
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
107
|
* Extracts all literal values from a usage description.
|
|
299
108
|
*
|
|
300
109
|
* This function recursively traverses the usage tree and collects all
|
|
@@ -769,9 +578,6 @@ exports.cloneUsage = cloneUsage;
|
|
|
769
578
|
exports.cloneUsageTerm = cloneUsageTerm;
|
|
770
579
|
exports.extractArgumentMetavars = extractArgumentMetavars;
|
|
771
580
|
exports.extractCommandNames = extractCommandNames;
|
|
772
|
-
exports.extractLeadingCommandNames = extractLeadingCommandNames;
|
|
773
|
-
exports.extractLeadingLiteralValues = extractLeadingLiteralValues;
|
|
774
|
-
exports.extractLeadingOptionNames = extractLeadingOptionNames;
|
|
775
581
|
exports.extractLiteralValues = extractLiteralValues;
|
|
776
582
|
exports.extractOptionNames = extractOptionNames;
|
|
777
583
|
exports.formatUsage = formatUsage;
|
package/dist/usage.d.cts
CHANGED
|
@@ -270,80 +270,6 @@ declare function extractOptionNames(usage: Usage, includeHidden?: boolean): Set<
|
|
|
270
270
|
* @since 0.7.0
|
|
271
271
|
*/
|
|
272
272
|
declare function extractCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
273
|
-
/**
|
|
274
|
-
* Extracts option names that are reachable at the leading token position
|
|
275
|
-
* (before any command or argument gate).
|
|
276
|
-
*
|
|
277
|
-
* Unlike {@link extractOptionNames}, which traverses the entire usage tree,
|
|
278
|
-
* this function stops scanning a terms array after encountering a `command`,
|
|
279
|
-
* `argument`, or `literal` term, because subsequent terms are scoped under
|
|
280
|
-
* that positional token. It still recurses into `optional`, `multiple`,
|
|
281
|
-
* and `exclusive` containers, since they represent alternatives or wrappers
|
|
282
|
-
* at the same position.
|
|
283
|
-
*
|
|
284
|
-
* Known limitation: this function infers token positions from the `usage`
|
|
285
|
-
* tree, which is a display-oriented structure. Combinators like `tuple()`
|
|
286
|
-
* and `object()` sort or flatten usage by priority rather than token order,
|
|
287
|
-
* so the results can be inaccurate—both false positives (e.g., options
|
|
288
|
-
* appearing before commands due to priority sorting) and false negatives
|
|
289
|
-
* (e.g., options after commands that are actually parallel peers).
|
|
290
|
-
* The proper fix is to use `Parser.leadingNames` instead of usage-tree
|
|
291
|
-
* analysis.
|
|
292
|
-
* See https://github.com/dahlia/optique/issues/735
|
|
293
|
-
*
|
|
294
|
-
* @param usage The usage description to extract leading option names from.
|
|
295
|
-
* @param includeHidden Whether to include fully hidden options
|
|
296
|
-
* (`hidden: true`) in the result. Defaults to `false`.
|
|
297
|
-
* @returns A set of option names reachable at the leading token position.
|
|
298
|
-
* @since 1.0.0
|
|
299
|
-
*/
|
|
300
|
-
declare function extractLeadingOptionNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
301
|
-
/**
|
|
302
|
-
* Extracts command names that could match as the first positional token.
|
|
303
|
-
*
|
|
304
|
-
* Unlike {@link extractCommandNames}, which traverses the entire usage tree,
|
|
305
|
-
* this function stops scanning a terms array after encountering a `command`,
|
|
306
|
-
* `argument`, or `literal` term, because subsequent terms in that array are
|
|
307
|
-
* scoped under that positional token (reachable only after the leading term
|
|
308
|
-
* is consumed). It still recurses into `optional`, `multiple`, and
|
|
309
|
-
* `exclusive` containers, since they represent alternatives or optional
|
|
310
|
-
* wrappers at the same token position.
|
|
311
|
-
*
|
|
312
|
-
* Known limitation: this function has the same usage-tree ordering caveat
|
|
313
|
-
* as {@link extractLeadingOptionNames}.
|
|
314
|
-
* See https://github.com/dahlia/optique/issues/735
|
|
315
|
-
*
|
|
316
|
-
* @param usage The usage description to extract leading command names from.
|
|
317
|
-
* @param includeHidden Whether to include fully hidden commands
|
|
318
|
-
* (`hidden: true`) in the result. Defaults to `false`.
|
|
319
|
-
* @returns A set of command names that could match at the first token position.
|
|
320
|
-
* @since 1.0.0
|
|
321
|
-
*/
|
|
322
|
-
declare function extractLeadingCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
|
|
323
|
-
/**
|
|
324
|
-
* Extracts literal values that could match as the first positional token.
|
|
325
|
-
*
|
|
326
|
-
* Unlike {@link extractLiteralValues}, which traverses the entire usage tree,
|
|
327
|
-
* this function stops scanning a terms array after encountering a `command`,
|
|
328
|
-
* `argument`, or `literal` term, because subsequent terms in that array are
|
|
329
|
-
* scoped under that positional token. It still recurses into `optional`,
|
|
330
|
-
* `multiple`, and `exclusive` containers.
|
|
331
|
-
*
|
|
332
|
-
* Literals tagged with `optionValue: true` (produced by
|
|
333
|
-
* `appendLiteralToUsage()` when rewriting option metavars for
|
|
334
|
-
* `conditional()` discriminators) are skipped, because they represent
|
|
335
|
-
* option values rather than standalone positional tokens.
|
|
336
|
-
*
|
|
337
|
-
* Known limitation: this function has the same usage-tree ordering caveat
|
|
338
|
-
* as {@link extractLeadingOptionNames}.
|
|
339
|
-
* See https://github.com/dahlia/optique/issues/735
|
|
340
|
-
*
|
|
341
|
-
* @param usage The usage description to extract leading literal values from.
|
|
342
|
-
* @returns A set of literal values that could match at the first token
|
|
343
|
-
* position.
|
|
344
|
-
* @since 1.0.0
|
|
345
|
-
*/
|
|
346
|
-
declare function extractLeadingLiteralValues(usage: Usage): Set<string>;
|
|
347
273
|
/**
|
|
348
274
|
* Extracts all literal values from a usage description.
|
|
349
275
|
*
|
|
@@ -520,4 +446,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
|
|
|
520
446
|
*/
|
|
521
447
|
declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
|
|
522
448
|
//#endregion
|
|
523
|
-
export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames,
|
|
449
|
+
export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
|