@optique/core 0.8.0-dev.165 → 0.8.0-dev.166
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 +108 -184
- package/dist/constructs.d.cts +10 -1
- package/dist/constructs.d.ts +10 -1
- package/dist/constructs.js +109 -186
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/modifiers.cjs +41 -58
- package/dist/modifiers.js +41 -58
- package/dist/parser.cjs +14 -9
- package/dist/parser.d.cts +2 -2
- package/dist/parser.d.ts +2 -2
- package/dist/parser.js +15 -11
- package/dist/suggestion.cjs +50 -0
- package/dist/suggestion.js +50 -1
- package/dist/valueparser.cjs +1 -1
- package/dist/valueparser.js +1 -1
- package/package.json +1 -1
package/dist/constructs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { message, optionName, values } from "./message.js";
|
|
2
2
|
import { extractArgumentMetavars, extractCommandNames, extractOptionNames } from "./usage.js";
|
|
3
|
-
import { createErrorWithSuggestions } from "./suggestion.js";
|
|
3
|
+
import { createErrorWithSuggestions, deduplicateSuggestions } from "./suggestion.js";
|
|
4
4
|
|
|
5
5
|
//#region src/constructs.ts
|
|
6
6
|
/**
|
|
@@ -47,6 +47,35 @@ function analyzeNoMatchContext(parsers) {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
+
* Error class thrown when duplicate option names are detected during parser
|
|
51
|
+
* construction. This is a programmer error, not a user error.
|
|
52
|
+
*/
|
|
53
|
+
var DuplicateOptionError = class extends Error {
|
|
54
|
+
constructor(optionName$1, sources) {
|
|
55
|
+
super(`Duplicate option name "${optionName$1}" found in fields: ${sources.join(", ")}. Each option name must be unique within a parser combinator.`);
|
|
56
|
+
this.optionName = optionName$1;
|
|
57
|
+
this.sources = sources;
|
|
58
|
+
this.name = "DuplicateOptionError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Checks for duplicate option names across parser sources and throws an error
|
|
63
|
+
* if duplicates are found. This should be called at construction time.
|
|
64
|
+
* @param parserSources Array of [source, usage] tuples
|
|
65
|
+
* @throws DuplicateOptionError if duplicate option names are found
|
|
66
|
+
*/
|
|
67
|
+
function checkDuplicateOptionNames(parserSources) {
|
|
68
|
+
const optionNameSources = /* @__PURE__ */ new Map();
|
|
69
|
+
for (const [source, usage] of parserSources) {
|
|
70
|
+
const names = extractOptionNames(usage);
|
|
71
|
+
for (const name of names) {
|
|
72
|
+
if (!optionNameSources.has(name)) optionNameSources.set(name, []);
|
|
73
|
+
optionNameSources.get(name).push(source);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const [name, sources] of optionNameSources) if (sources.length > 1) throw new DuplicateOptionError(name, sources);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
50
79
|
* Generates a contextual error message based on what types of inputs
|
|
51
80
|
* the parsers expect (options, commands, or arguments).
|
|
52
81
|
* @param context Context about what types of inputs are expected
|
|
@@ -63,6 +92,70 @@ function generateNoMatchError(context) {
|
|
|
63
92
|
else return message`No matching option, command, or argument found.`;
|
|
64
93
|
}
|
|
65
94
|
/**
|
|
95
|
+
* Creates a complete() method shared by or() and longestMatch().
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
function createExclusiveComplete(parsers, options, noMatchContext) {
|
|
99
|
+
return (state) => {
|
|
100
|
+
if (state == null) return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: getNoMatchError(options, noMatchContext)
|
|
103
|
+
};
|
|
104
|
+
const [i, result] = state;
|
|
105
|
+
if (result.success) return parsers[i].complete(result.next.state);
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
error: result.error
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Creates a suggest() method shared by or() and longestMatch().
|
|
114
|
+
* @internal
|
|
115
|
+
*/
|
|
116
|
+
function createExclusiveSuggest(parsers) {
|
|
117
|
+
return (context, prefix) => {
|
|
118
|
+
const suggestions = [];
|
|
119
|
+
if (context.state == null) for (const parser of parsers) {
|
|
120
|
+
const parserSuggestions = parser.suggest({
|
|
121
|
+
...context,
|
|
122
|
+
state: parser.initialState
|
|
123
|
+
}, prefix);
|
|
124
|
+
suggestions.push(...parserSuggestions);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const [index, parserResult] = context.state;
|
|
128
|
+
if (parserResult.success) {
|
|
129
|
+
const parserSuggestions = parsers[index].suggest({
|
|
130
|
+
...context,
|
|
131
|
+
state: parserResult.next.state
|
|
132
|
+
}, prefix);
|
|
133
|
+
suggestions.push(...parserSuggestions);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return deduplicateSuggestions(suggestions);
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Gets the no-match error, either from custom options or default.
|
|
141
|
+
* Shared by or() and longestMatch().
|
|
142
|
+
* @internal
|
|
143
|
+
*/
|
|
144
|
+
function getNoMatchError(options, noMatchContext) {
|
|
145
|
+
const customNoMatch = options?.errors?.noMatch;
|
|
146
|
+
return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Creates default error for parse() method when buffer is not empty.
|
|
150
|
+
* Shared by or() and longestMatch().
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
function createUnexpectedInputError(token, usage, options) {
|
|
154
|
+
const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
|
|
155
|
+
if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
|
|
156
|
+
return createErrorWithSuggestions(defaultMsg, token, usage, "both", options?.errors?.suggestions);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
66
159
|
* @since 0.5.0
|
|
67
160
|
*/
|
|
68
161
|
function or(...args) {
|
|
@@ -85,34 +178,11 @@ function or(...args) {
|
|
|
85
178
|
terms: parsers.map((p) => p.usage)
|
|
86
179
|
}],
|
|
87
180
|
initialState: void 0,
|
|
88
|
-
complete(
|
|
89
|
-
if (state == null) {
|
|
90
|
-
const customNoMatch = options?.errors?.noMatch;
|
|
91
|
-
const error = customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
|
|
92
|
-
return {
|
|
93
|
-
success: false,
|
|
94
|
-
error
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
const [i, result] = state;
|
|
98
|
-
if (result.success) return parsers[i].complete(result.next.state);
|
|
99
|
-
return {
|
|
100
|
-
success: false,
|
|
101
|
-
error: result.error
|
|
102
|
-
};
|
|
103
|
-
},
|
|
181
|
+
complete: createExclusiveComplete(parsers, options, noMatchContext),
|
|
104
182
|
parse(context) {
|
|
105
183
|
let error = {
|
|
106
184
|
consumed: 0,
|
|
107
|
-
error: context.buffer.length < 1 ? (
|
|
108
|
-
const customNoMatch = options?.errors?.noMatch;
|
|
109
|
-
return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
|
|
110
|
-
})() : (() => {
|
|
111
|
-
const token = context.buffer[0];
|
|
112
|
-
const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
|
|
113
|
-
if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
|
|
114
|
-
return createErrorWithSuggestions(defaultMsg, token, context.usage, "both", options?.errors?.suggestions);
|
|
115
|
-
})()
|
|
185
|
+
error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : createUnexpectedInputError(context.buffer[0], context.usage, options)
|
|
116
186
|
};
|
|
117
187
|
const orderedParsers = parsers.map((p, i) => [p, i]);
|
|
118
188
|
orderedParsers.sort(([_, a], [__, b]) => context.state?.[0] === a ? -1 : context.state?.[0] === b ? 1 : a - b);
|
|
@@ -144,33 +214,7 @@ function or(...args) {
|
|
|
144
214
|
success: false
|
|
145
215
|
};
|
|
146
216
|
},
|
|
147
|
-
suggest(
|
|
148
|
-
const suggestions = [];
|
|
149
|
-
if (context.state == null) for (const parser of parsers) {
|
|
150
|
-
const parserSuggestions = parser.suggest({
|
|
151
|
-
...context,
|
|
152
|
-
state: parser.initialState
|
|
153
|
-
}, prefix);
|
|
154
|
-
suggestions.push(...parserSuggestions);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
const [index, parserResult] = context.state;
|
|
158
|
-
if (parserResult.success) {
|
|
159
|
-
const parserSuggestions = parsers[index].suggest({
|
|
160
|
-
...context,
|
|
161
|
-
state: parserResult.next.state
|
|
162
|
-
}, prefix);
|
|
163
|
-
suggestions.push(...parserSuggestions);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
const seen = /* @__PURE__ */ new Set();
|
|
167
|
-
return suggestions.filter((suggestion) => {
|
|
168
|
-
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
169
|
-
if (seen.has(key)) return false;
|
|
170
|
-
seen.add(key);
|
|
171
|
-
return true;
|
|
172
|
-
});
|
|
173
|
-
},
|
|
217
|
+
suggest: createExclusiveSuggest(parsers),
|
|
174
218
|
getDocFragments(state, _defaultValue) {
|
|
175
219
|
let description;
|
|
176
220
|
let fragments;
|
|
@@ -228,35 +272,12 @@ function longestMatch(...args) {
|
|
|
228
272
|
terms: parsers.map((p) => p.usage)
|
|
229
273
|
}],
|
|
230
274
|
initialState: void 0,
|
|
231
|
-
complete(
|
|
232
|
-
if (state == null) {
|
|
233
|
-
const customNoMatch = options?.errors?.noMatch;
|
|
234
|
-
const error = customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
|
|
235
|
-
return {
|
|
236
|
-
success: false,
|
|
237
|
-
error
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
const [i, result] = state;
|
|
241
|
-
if (result.success) return parsers[i].complete(result.next.state);
|
|
242
|
-
return {
|
|
243
|
-
success: false,
|
|
244
|
-
error: result.error
|
|
245
|
-
};
|
|
246
|
-
},
|
|
275
|
+
complete: createExclusiveComplete(parsers, options, noMatchContext),
|
|
247
276
|
parse(context) {
|
|
248
277
|
let bestMatch = null;
|
|
249
278
|
let error = {
|
|
250
279
|
consumed: 0,
|
|
251
|
-
error: context.buffer.length < 1 ? (
|
|
252
|
-
const customNoMatch = options?.errors?.noMatch;
|
|
253
|
-
return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
|
|
254
|
-
})() : (() => {
|
|
255
|
-
const token = context.buffer[0];
|
|
256
|
-
const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
|
|
257
|
-
if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
|
|
258
|
-
return createErrorWithSuggestions(defaultMsg, token, context.usage, "both", options?.errors?.suggestions);
|
|
259
|
-
})()
|
|
280
|
+
error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : createUnexpectedInputError(context.buffer[0], context.usage, options)
|
|
260
281
|
};
|
|
261
282
|
for (let i = 0; i < parsers.length; i++) {
|
|
262
283
|
const parser = parsers[i];
|
|
@@ -288,33 +309,7 @@ function longestMatch(...args) {
|
|
|
288
309
|
success: false
|
|
289
310
|
};
|
|
290
311
|
},
|
|
291
|
-
suggest(
|
|
292
|
-
const suggestions = [];
|
|
293
|
-
if (context.state == null) for (const parser of parsers) {
|
|
294
|
-
const parserSuggestions = parser.suggest({
|
|
295
|
-
...context,
|
|
296
|
-
state: parser.initialState
|
|
297
|
-
}, prefix);
|
|
298
|
-
suggestions.push(...parserSuggestions);
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
const [index, parserResult] = context.state;
|
|
302
|
-
if (parserResult.success) {
|
|
303
|
-
const parserSuggestions = parsers[index].suggest({
|
|
304
|
-
...context,
|
|
305
|
-
state: parserResult.next.state
|
|
306
|
-
}, prefix);
|
|
307
|
-
suggestions.push(...parserSuggestions);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
const seen = /* @__PURE__ */ new Set();
|
|
311
|
-
return suggestions.filter((suggestion) => {
|
|
312
|
-
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
313
|
-
if (seen.has(key)) return false;
|
|
314
|
-
seen.add(key);
|
|
315
|
-
return true;
|
|
316
|
-
});
|
|
317
|
-
},
|
|
312
|
+
suggest: createExclusiveSuggest(parsers),
|
|
318
313
|
getDocFragments(state, _defaultValue) {
|
|
319
314
|
let description;
|
|
320
315
|
let footer;
|
|
@@ -353,6 +348,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
353
348
|
}
|
|
354
349
|
const parserPairs = Object.entries(parsers);
|
|
355
350
|
parserPairs.sort(([_, parserA], [__, parserB]) => parserB.priority - parserA.priority);
|
|
351
|
+
if (!options.allowDuplicates) checkDuplicateOptionNames(parserPairs.map(([field, parser]) => [field, parser.usage]));
|
|
356
352
|
const noMatchContext = analyzeNoMatchContext(Object.values(parsers));
|
|
357
353
|
return {
|
|
358
354
|
$valueType: [],
|
|
@@ -361,21 +357,6 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
361
357
|
usage: parserPairs.flatMap(([_, p]) => p.usage),
|
|
362
358
|
initialState: Object.fromEntries(Object.entries(parsers).map(([key, parser]) => [key, parser.initialState])),
|
|
363
359
|
parse(context) {
|
|
364
|
-
if (!options.allowDuplicates) {
|
|
365
|
-
const optionNameSources = /* @__PURE__ */ new Map();
|
|
366
|
-
for (const [field, parser] of parserPairs) {
|
|
367
|
-
const names = extractOptionNames(parser.usage);
|
|
368
|
-
for (const name of names) {
|
|
369
|
-
if (!optionNameSources.has(name)) optionNameSources.set(name, []);
|
|
370
|
-
optionNameSources.get(name).push(field);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
for (const [name, sources] of optionNameSources) if (sources.length > 1) return {
|
|
374
|
-
success: false,
|
|
375
|
-
consumed: 0,
|
|
376
|
-
error: message`Duplicate option name ${optionName(name)} found in fields: ${values(sources)}. Each option name must be unique within a parser combinator.`
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
360
|
let error = {
|
|
380
361
|
consumed: 0,
|
|
381
362
|
error: context.buffer.length > 0 ? (() => {
|
|
@@ -469,13 +450,7 @@ function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
469
450
|
}, prefix);
|
|
470
451
|
suggestions.push(...fieldSuggestions);
|
|
471
452
|
}
|
|
472
|
-
|
|
473
|
-
return suggestions.filter((suggestion) => {
|
|
474
|
-
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
475
|
-
if (seen.has(key)) return false;
|
|
476
|
-
seen.add(key);
|
|
477
|
-
return true;
|
|
478
|
-
});
|
|
453
|
+
return deduplicateSuggestions(suggestions);
|
|
479
454
|
},
|
|
480
455
|
getDocFragments(state, defaultValue) {
|
|
481
456
|
const fragments = parserPairs.flatMap(([field, p]) => {
|
|
@@ -515,6 +490,7 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
515
490
|
parsers = labelOrParsers;
|
|
516
491
|
options = maybeParsersOrOptions ?? {};
|
|
517
492
|
}
|
|
493
|
+
if (!options.allowDuplicates) checkDuplicateOptionNames(parsers.map((parser, index) => [String(index), parser.usage]));
|
|
518
494
|
return {
|
|
519
495
|
$valueType: [],
|
|
520
496
|
$stateType: [],
|
|
@@ -522,21 +498,6 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
522
498
|
priority: parsers.length > 0 ? Math.max(...parsers.map((p) => p.priority)) : 0,
|
|
523
499
|
initialState: parsers.map((parser) => parser.initialState),
|
|
524
500
|
parse(context) {
|
|
525
|
-
if (!options.allowDuplicates) {
|
|
526
|
-
const optionNameSources = /* @__PURE__ */ new Map();
|
|
527
|
-
for (let i = 0; i < parsers.length; i++) {
|
|
528
|
-
const names = extractOptionNames(parsers[i].usage);
|
|
529
|
-
for (const name of names) {
|
|
530
|
-
if (!optionNameSources.has(name)) optionNameSources.set(name, []);
|
|
531
|
-
optionNameSources.get(name).push(i);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
for (const [name, indices] of optionNameSources) if (indices.length > 1) return {
|
|
535
|
-
success: false,
|
|
536
|
-
consumed: 0,
|
|
537
|
-
error: message`Duplicate option name ${optionName(name)} found at positions: ${values(indices.map(String))}. Each option name must be unique within a parser combinator.`
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
501
|
let currentContext = context;
|
|
541
502
|
const allConsumed = [];
|
|
542
503
|
const matchedParsers = /* @__PURE__ */ new Set();
|
|
@@ -621,13 +582,7 @@ function tuple(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
|
621
582
|
}, prefix);
|
|
622
583
|
suggestions.push(...parserSuggestions);
|
|
623
584
|
}
|
|
624
|
-
|
|
625
|
-
return suggestions.filter((suggestion) => {
|
|
626
|
-
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
627
|
-
if (seen.has(key)) return false;
|
|
628
|
-
seen.add(key);
|
|
629
|
-
return true;
|
|
630
|
-
});
|
|
585
|
+
return deduplicateSuggestions(suggestions);
|
|
631
586
|
},
|
|
632
587
|
getDocFragments(state, defaultValue) {
|
|
633
588
|
const fragments = parsers.flatMap((p, i) => {
|
|
@@ -670,6 +625,7 @@ function merge(...args) {
|
|
|
670
625
|
const withIndex = rawParsers.map((p, i) => [p, i]);
|
|
671
626
|
const sorted = withIndex.toSorted(([a], [b]) => b.priority - a.priority);
|
|
672
627
|
const parsers = sorted.map(([p]) => p);
|
|
628
|
+
if (!options.allowDuplicates) checkDuplicateOptionNames(sorted.map(([parser, originalIndex]) => [String(originalIndex), parser.usage]));
|
|
673
629
|
const initialState = {};
|
|
674
630
|
for (const parser of parsers) if (parser.initialState && typeof parser.initialState === "object") for (const field in parser.initialState) initialState[field] = parser.initialState[field];
|
|
675
631
|
return {
|
|
@@ -679,21 +635,6 @@ function merge(...args) {
|
|
|
679
635
|
usage: parsers.flatMap((p) => p.usage),
|
|
680
636
|
initialState,
|
|
681
637
|
parse(context) {
|
|
682
|
-
if (!options.allowDuplicates) {
|
|
683
|
-
const optionNameSources = /* @__PURE__ */ new Map();
|
|
684
|
-
for (const [parser, originalIndex] of sorted) {
|
|
685
|
-
const names = extractOptionNames(parser.usage);
|
|
686
|
-
for (const name of names) {
|
|
687
|
-
if (!optionNameSources.has(name)) optionNameSources.set(name, []);
|
|
688
|
-
optionNameSources.get(name).push(originalIndex);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
for (const [name, indices] of optionNameSources) if (indices.length > 1) return {
|
|
692
|
-
success: false,
|
|
693
|
-
consumed: 0,
|
|
694
|
-
error: message`Duplicate option name ${optionName(name)} found in merged parsers at positions: ${values(indices.map(String))}. Each option name must be unique within a parser combinator.`
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
638
|
for (let i = 0; i < parsers.length; i++) {
|
|
698
639
|
const parser = parsers[i];
|
|
699
640
|
let parserState;
|
|
@@ -782,13 +723,7 @@ function merge(...args) {
|
|
|
782
723
|
}, prefix);
|
|
783
724
|
suggestions.push(...parserSuggestions);
|
|
784
725
|
}
|
|
785
|
-
|
|
786
|
-
return suggestions.filter((suggestion) => {
|
|
787
|
-
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
788
|
-
if (seen.has(key)) return false;
|
|
789
|
-
seen.add(key);
|
|
790
|
-
return true;
|
|
791
|
-
});
|
|
726
|
+
return deduplicateSuggestions(suggestions);
|
|
792
727
|
},
|
|
793
728
|
getDocFragments(state, _defaultValue) {
|
|
794
729
|
const fragments = parsers.flatMap((p) => {
|
|
@@ -919,13 +854,7 @@ function concat(...parsers) {
|
|
|
919
854
|
}, prefix);
|
|
920
855
|
suggestions.push(...parserSuggestions);
|
|
921
856
|
}
|
|
922
|
-
|
|
923
|
-
return suggestions.filter((suggestion) => {
|
|
924
|
-
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}:${suggestion.pattern}`;
|
|
925
|
-
if (seen.has(key)) return false;
|
|
926
|
-
seen.add(key);
|
|
927
|
-
return true;
|
|
928
|
-
});
|
|
857
|
+
return deduplicateSuggestions(suggestions);
|
|
929
858
|
},
|
|
930
859
|
getDocFragments(state, _defaultValue) {
|
|
931
860
|
const fragments = parsers.flatMap((p, index) => {
|
|
@@ -1257,13 +1186,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
1257
1186
|
state: state.branchState
|
|
1258
1187
|
}, prefix));
|
|
1259
1188
|
}
|
|
1260
|
-
|
|
1261
|
-
return suggestions.filter((suggestion) => {
|
|
1262
|
-
const key = suggestion.kind === "literal" ? suggestion.text : `__FILE__:${suggestion.type}:${suggestion.extensions?.join(",")}`;
|
|
1263
|
-
if (seen.has(key)) return false;
|
|
1264
|
-
seen.add(key);
|
|
1265
|
-
return true;
|
|
1266
|
-
});
|
|
1189
|
+
return deduplicateSuggestions(suggestions);
|
|
1267
1190
|
},
|
|
1268
1191
|
getDocFragments(_state, _defaultValue) {
|
|
1269
1192
|
const fragments = [];
|
|
@@ -1295,4 +1218,4 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
1295
1218
|
}
|
|
1296
1219
|
|
|
1297
1220
|
//#endregion
|
|
1298
|
-
export { concat, conditional, group, longestMatch, merge, object, or, tuple };
|
|
1221
|
+
export { DuplicateOptionError, concat, conditional, group, longestMatch, merge, object, or, tuple };
|
package/dist/index.cjs
CHANGED
|
@@ -9,6 +9,7 @@ const require_primitives = require('./primitives.cjs');
|
|
|
9
9
|
const require_parser = require('./parser.cjs');
|
|
10
10
|
const require_facade = require('./facade.cjs');
|
|
11
11
|
|
|
12
|
+
exports.DuplicateOptionError = require_constructs.DuplicateOptionError;
|
|
12
13
|
exports.RunError = require_facade.RunError;
|
|
13
14
|
exports.WithDefaultError = require_modifiers.WithDefaultError;
|
|
14
15
|
exports.argument = require_primitives.argument;
|
package/dist/index.d.cts
CHANGED
|
@@ -5,7 +5,7 @@ import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber
|
|
|
5
5
|
import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, optional, withDefault } from "./modifiers.cjs";
|
|
6
6
|
import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, PassThroughFormat, PassThroughOptions, argument, command, constant, flag, option, passThrough } from "./primitives.cjs";
|
|
7
7
|
import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, Suggestion, getDocPage, parse, suggest } from "./parser.cjs";
|
|
8
|
-
import { ConditionalErrorOptions, ConditionalOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
|
|
8
|
+
import { ConditionalErrorOptions, ConditionalOptions, DuplicateOptionError, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
|
|
9
9
|
import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.cjs";
|
|
10
10
|
import { RunError, RunOptions, run } from "./facade.cjs";
|
|
11
|
-
export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, ConditionalErrorOptions, ConditionalOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, PassThroughFormat, PassThroughOptions, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, commandLine, concat, conditional, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, passThrough, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
|
|
11
|
+
export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, ConditionalErrorOptions, ConditionalOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DuplicateOptionError, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, PassThroughFormat, PassThroughOptions, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, commandLine, concat, conditional, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, passThrough, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber
|
|
|
5
5
|
import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, optional, withDefault } from "./modifiers.js";
|
|
6
6
|
import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, PassThroughFormat, PassThroughOptions, argument, command, constant, flag, option, passThrough } from "./primitives.js";
|
|
7
7
|
import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, Suggestion, getDocPage, parse, suggest } from "./parser.js";
|
|
8
|
-
import { ConditionalErrorOptions, ConditionalOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
|
|
8
|
+
import { ConditionalErrorOptions, ConditionalOptions, DuplicateOptionError, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
|
|
9
9
|
import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.js";
|
|
10
10
|
import { RunError, RunOptions, run } from "./facade.js";
|
|
11
|
-
export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, ConditionalErrorOptions, ConditionalOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, PassThroughFormat, PassThroughOptions, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, commandLine, concat, conditional, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, passThrough, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
|
|
11
|
+
export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, ConditionalErrorOptions, ConditionalOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, DuplicateOptionError, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, PassThroughFormat, PassThroughOptions, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, TupleOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, commandLine, concat, conditional, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, passThrough, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { commandLine, envVar, formatMessage, message, metavar, optionName, optionNames, text, value, values } from "./message.js";
|
|
2
2
|
import { extractArgumentMetavars, extractCommandNames, extractOptionNames, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
|
|
3
|
-
import { concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
|
|
3
|
+
import { DuplicateOptionError, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
|
|
4
4
|
import { formatDocPage } from "./doc.js";
|
|
5
5
|
import { bash, fish, nu, pwsh, zsh } from "./completion.js";
|
|
6
6
|
import { WithDefaultError, map, multiple, optional, withDefault } from "./modifiers.js";
|
|
@@ -9,4 +9,4 @@ import { argument, command, constant, flag, option, passThrough } from "./primit
|
|
|
9
9
|
import { getDocPage, parse, suggest } from "./parser.js";
|
|
10
10
|
import { RunError, run } from "./facade.js";
|
|
11
11
|
|
|
12
|
-
export { RunError, WithDefaultError, argument, bash, choice, command, commandLine, concat, conditional, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, passThrough, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
|
|
12
|
+
export { DuplicateOptionError, RunError, WithDefaultError, argument, bash, choice, command, commandLine, concat, conditional, constant, envVar, extractArgumentMetavars, extractCommandNames, extractOptionNames, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, passThrough, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
|
package/dist/modifiers.cjs
CHANGED
|
@@ -2,6 +2,45 @@ const require_message = require('./message.cjs');
|
|
|
2
2
|
|
|
3
3
|
//#region src/modifiers.ts
|
|
4
4
|
/**
|
|
5
|
+
* Internal helper for optional-style parsing logic shared by optional()
|
|
6
|
+
* and withDefault(). Handles the common pattern of:
|
|
7
|
+
* - Unwrapping optional state to inner parser state
|
|
8
|
+
* - Detecting if inner parser actually matched (state changed or no consumption)
|
|
9
|
+
* - Returning success with undefined state when inner parser fails without consuming
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
function parseOptionalStyle(context, parser) {
|
|
13
|
+
const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
|
|
14
|
+
const result = parser.parse({
|
|
15
|
+
...context,
|
|
16
|
+
state: innerState
|
|
17
|
+
});
|
|
18
|
+
if (result.success) {
|
|
19
|
+
if (result.next.state !== innerState || result.consumed.length === 0) return {
|
|
20
|
+
success: true,
|
|
21
|
+
next: {
|
|
22
|
+
...result.next,
|
|
23
|
+
state: [result.next.state]
|
|
24
|
+
},
|
|
25
|
+
consumed: result.consumed
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
next: {
|
|
30
|
+
...result.next,
|
|
31
|
+
state: context.state
|
|
32
|
+
},
|
|
33
|
+
consumed: result.consumed
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (result.consumed === 0) return {
|
|
37
|
+
success: true,
|
|
38
|
+
next: context,
|
|
39
|
+
consumed: []
|
|
40
|
+
};
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
5
44
|
* Creates a parser that makes another parser optional, allowing it to succeed
|
|
6
45
|
* without consuming input if the wrapped parser fails to match.
|
|
7
46
|
* If the wrapped parser succeeds, this returns its value.
|
|
@@ -23,35 +62,7 @@ function optional(parser) {
|
|
|
23
62
|
}],
|
|
24
63
|
initialState: void 0,
|
|
25
64
|
parse(context) {
|
|
26
|
-
|
|
27
|
-
const result = parser.parse({
|
|
28
|
-
...context,
|
|
29
|
-
state: innerState
|
|
30
|
-
});
|
|
31
|
-
if (result.success) {
|
|
32
|
-
if (result.next.state !== innerState || result.consumed.length === 0) return {
|
|
33
|
-
success: true,
|
|
34
|
-
next: {
|
|
35
|
-
...result.next,
|
|
36
|
-
state: [result.next.state]
|
|
37
|
-
},
|
|
38
|
-
consumed: result.consumed
|
|
39
|
-
};
|
|
40
|
-
return {
|
|
41
|
-
success: true,
|
|
42
|
-
next: {
|
|
43
|
-
...result.next,
|
|
44
|
-
state: context.state
|
|
45
|
-
},
|
|
46
|
-
consumed: result.consumed
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
if (result.consumed === 0) return {
|
|
50
|
-
success: true,
|
|
51
|
-
next: context,
|
|
52
|
-
consumed: []
|
|
53
|
-
};
|
|
54
|
-
return result;
|
|
65
|
+
return parseOptionalStyle(context, parser);
|
|
55
66
|
},
|
|
56
67
|
complete(state) {
|
|
57
68
|
if (typeof state === "undefined") return {
|
|
@@ -121,35 +132,7 @@ function withDefault(parser, defaultValue, options) {
|
|
|
121
132
|
}],
|
|
122
133
|
initialState: void 0,
|
|
123
134
|
parse(context) {
|
|
124
|
-
|
|
125
|
-
const result = parser.parse({
|
|
126
|
-
...context,
|
|
127
|
-
state: innerState
|
|
128
|
-
});
|
|
129
|
-
if (result.success) {
|
|
130
|
-
if (result.next.state !== innerState || result.consumed.length === 0) return {
|
|
131
|
-
success: true,
|
|
132
|
-
next: {
|
|
133
|
-
...result.next,
|
|
134
|
-
state: [result.next.state]
|
|
135
|
-
},
|
|
136
|
-
consumed: result.consumed
|
|
137
|
-
};
|
|
138
|
-
return {
|
|
139
|
-
success: true,
|
|
140
|
-
next: {
|
|
141
|
-
...result.next,
|
|
142
|
-
state: context.state
|
|
143
|
-
},
|
|
144
|
-
consumed: result.consumed
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
if (result.consumed === 0) return {
|
|
148
|
-
success: true,
|
|
149
|
-
next: context,
|
|
150
|
-
consumed: []
|
|
151
|
-
};
|
|
152
|
-
return result;
|
|
135
|
+
return parseOptionalStyle(context, parser);
|
|
153
136
|
},
|
|
154
137
|
complete(state) {
|
|
155
138
|
if (typeof state === "undefined") try {
|