@optique/core 0.10.0-dev.374 → 0.10.0-dev.375

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/facade.cjs CHANGED
@@ -113,10 +113,7 @@ function createCompletionParser(mode, programName, availableShells, name = "both
113
113
  };
114
114
  }
115
115
  }
116
- /**
117
- * Systematically combines the original parser with help, version, and completion parsers.
118
- */
119
- function combineWithHelpVersion(originalParser, helpParsers, versionParsers, completionParsers) {
116
+ function combineWithHelpVersion(originalParser, helpParsers, versionParsers, completionParsers, groups) {
120
117
  const parsers = [];
121
118
  if (helpParsers.helpOption) {
122
119
  const lenientHelpParser = {
@@ -262,26 +259,35 @@ function combineWithHelpVersion(originalParser, helpParsers, versionParsers, com
262
259
  };
263
260
  parsers.push(lenientVersionParser);
264
261
  }
265
- if (versionParsers.versionCommand) parsers.push(require_constructs.object({
266
- help: require_primitives.constant(false),
267
- version: require_primitives.constant(true),
268
- completion: require_primitives.constant(false),
269
- result: versionParsers.versionCommand,
270
- helpFlag: helpParsers.helpOption ? require_modifiers.optional(helpParsers.helpOption) : require_primitives.constant(false)
271
- }));
272
- if (completionParsers.completionCommand) parsers.push(require_constructs.object({
273
- help: require_primitives.constant(false),
274
- version: require_primitives.constant(false),
275
- completion: require_primitives.constant(true),
276
- completionData: completionParsers.completionCommand,
277
- helpFlag: helpParsers.helpOption ? require_modifiers.optional(helpParsers.helpOption) : require_primitives.constant(false)
278
- }));
279
- if (helpParsers.helpCommand) parsers.push(require_constructs.object({
280
- help: require_primitives.constant(true),
281
- version: require_primitives.constant(false),
282
- completion: require_primitives.constant(false),
283
- commands: helpParsers.helpCommand
284
- }));
262
+ if (versionParsers.versionCommand) {
263
+ const versionParser = require_constructs.object({
264
+ help: require_primitives.constant(false),
265
+ version: require_primitives.constant(true),
266
+ completion: require_primitives.constant(false),
267
+ result: versionParsers.versionCommand,
268
+ helpFlag: helpParsers.helpOption ? require_modifiers.optional(helpParsers.helpOption) : require_primitives.constant(false)
269
+ });
270
+ parsers.push(groups?.versionGroup ? require_constructs.group(groups.versionGroup, versionParser) : versionParser);
271
+ }
272
+ if (completionParsers.completionCommand) {
273
+ const completionParser = require_constructs.object({
274
+ help: require_primitives.constant(false),
275
+ version: require_primitives.constant(false),
276
+ completion: require_primitives.constant(true),
277
+ completionData: completionParsers.completionCommand,
278
+ helpFlag: helpParsers.helpOption ? require_modifiers.optional(helpParsers.helpOption) : require_primitives.constant(false)
279
+ });
280
+ parsers.push(groups?.completionGroup ? require_constructs.group(groups.completionGroup, completionParser) : completionParser);
281
+ }
282
+ if (helpParsers.helpCommand) {
283
+ const helpParser = require_constructs.object({
284
+ help: require_primitives.constant(true),
285
+ version: require_primitives.constant(false),
286
+ completion: require_primitives.constant(false),
287
+ commands: helpParsers.helpCommand
288
+ });
289
+ parsers.push(groups?.helpGroup ? require_constructs.group(groups.helpGroup, helpParser) : helpParser);
290
+ }
285
291
  parsers.push(require_constructs.object({
286
292
  help: require_primitives.constant(false),
287
293
  version: require_primitives.constant(false),
@@ -456,13 +462,16 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
456
462
  }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
457
463
  const helpMode = options.help?.mode ?? "option";
458
464
  const onHelp = options.help?.onShow ?? (() => ({}));
465
+ const helpGroup = options.help?.group;
459
466
  const versionMode = options.version?.mode ?? "option";
460
467
  const versionValue = options.version?.value ?? "";
461
468
  const onVersion = options.version?.onShow ?? (() => ({}));
469
+ const versionGroup = options.version?.group;
462
470
  const completionMode = options.completion?.mode ?? "both";
463
471
  const completionName = options.completion?.name ?? "both";
464
472
  const completionHelpVisibility = options.completion?.helpVisibility ?? completionName;
465
473
  const onCompletion = options.completion?.onShow ?? (() => ({}));
474
+ const completionGroup = options.completion?.group;
466
475
  const defaultShells = {
467
476
  bash: require_completion.bash,
468
477
  fish: require_completion.fish,
@@ -511,7 +520,11 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
511
520
  }
512
521
  }
513
522
  }
514
- const augmentedParser = help === "none" && version === "none" && completion === "none" ? parser : combineWithHelpVersion(parser, helpParsers, versionParsers, completionParsers);
523
+ const augmentedParser = help === "none" && version === "none" && completion === "none" ? parser : combineWithHelpVersion(parser, helpParsers, versionParsers, completionParsers, {
524
+ helpGroup,
525
+ versionGroup,
526
+ completionGroup
527
+ });
515
528
  const handleResult = (result) => {
516
529
  const classified = classifyResult(result, args);
517
530
  switch (classified.type) {
@@ -535,15 +548,18 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
535
548
  else if (requestedCommand === "version" && versionAsCommand && versionParsers.versionCommand) helpGeneratorParser = versionParsers.versionCommand;
536
549
  else {
537
550
  const commandParsers = [parser];
538
- if (helpAsCommand) {
539
- if (helpParsers.helpCommand) commandParsers.push(helpParsers.helpCommand);
540
- }
541
- if (versionAsCommand) {
542
- if (versionParsers.versionCommand) commandParsers.push(versionParsers.versionCommand);
543
- }
544
- if (completionAsCommand) {
545
- if (completionParsers.completionCommand) commandParsers.push(completionParsers.completionCommand);
546
- }
551
+ const groupedMeta = {};
552
+ const ungroupedMeta = [];
553
+ const addMeta = (p, groupLabel) => {
554
+ if (groupLabel) (groupedMeta[groupLabel] ??= []).push(p);
555
+ else ungroupedMeta.push(p);
556
+ };
557
+ if (helpAsCommand && helpParsers.helpCommand) addMeta(helpParsers.helpCommand, helpGroup);
558
+ if (versionAsCommand && versionParsers.versionCommand) addMeta(versionParsers.versionCommand, versionGroup);
559
+ if (completionAsCommand && completionParsers.completionCommand) addMeta(completionParsers.completionCommand, completionGroup);
560
+ commandParsers.push(...ungroupedMeta);
561
+ for (const [label, parsers] of Object.entries(groupedMeta)) if (parsers.length === 1) commandParsers.push(require_constructs.group(label, parsers[0]));
562
+ else commandParsers.push(require_constructs.group(label, require_constructs.longestMatch(...parsers)));
547
563
  if (commandParsers.length === 1) helpGeneratorParser = commandParsers[0];
548
564
  else if (commandParsers.length === 2) helpGeneratorParser = require_constructs.longestMatch(commandParsers[0], commandParsers[1]);
549
565
  else helpGeneratorParser = require_constructs.longestMatch(...commandParsers);
package/dist/facade.d.cts CHANGED
@@ -24,30 +24,51 @@ type CompletionConfigBase<THelp> = {
24
24
  * Determines how completion is made available:
25
25
  *
26
26
  * - `"command"`: Only the `completion` subcommand is available
27
- * - `"option"`: Only the `--completion` option is available
28
- * - `"both"`: Both `completion` subcommand and `--completion` option are available
27
+ * - `"both"`: Both `completion` subcommand and `--completion` option
28
+ * are available
29
29
  *
30
30
  * @default `"both"`
31
31
  */
32
- readonly mode?: "command" | "option" | "both";
32
+ readonly mode?: "command" | "both";
33
+ /**
34
+ * Group label for the completion command in help output. When specified,
35
+ * the completion command appears under a titled section with this name
36
+ * instead of alongside user-defined commands.
37
+ *
38
+ * @since 0.10.0
39
+ */
40
+ readonly group?: string;
33
41
  /**
34
42
  * Available shell completions. By default, includes `bash`, `fish`, `nu`,
35
- * `pwsh`, and `zsh`. You can provide additional custom shell completions or
36
- * override the defaults.
43
+ * `pwsh`, and `zsh`. You can provide additional custom shell completions
44
+ * or override the defaults.
37
45
  *
38
46
  * @default `{ bash, fish, nu, pwsh, zsh }`
39
47
  */
40
48
  readonly shells?: Record<string, ShellCompletion>;
41
49
  /**
42
- * Callback function invoked when completion is requested. The function can
43
- * optionally receive an exit code parameter.
50
+ * Callback function invoked when completion is requested. The function
51
+ * can optionally receive an exit code parameter.
44
52
  *
45
- * You usually want to pass `process.exit` on Node.js or Bun and `Deno.exit`
46
- * on Deno to this option.
53
+ * You usually want to pass `process.exit` on Node.js or Bun and
54
+ * `Deno.exit` on Deno to this option.
47
55
  *
48
56
  * @default Returns `void` when completion is shown.
49
57
  */
50
58
  readonly onShow?: (() => THelp) | ((exitCode: number) => THelp);
59
+ } | {
60
+ /**
61
+ * Determines how completion is made available:
62
+ *
63
+ * - `"option"`: Only the `--completion` option is available
64
+ */
65
+ readonly mode: "option";
66
+ /** @since 0.10.0 */
67
+ readonly group?: never;
68
+ /** @default `{ bash, fish, nu, pwsh, zsh }` */
69
+ readonly shells?: Record<string, ShellCompletion>;
70
+ /** @default Returns `void` when completion is shown. */
71
+ readonly onShow?: (() => THelp) | ((exitCode: number) => THelp);
51
72
  };
52
73
  type CompletionConfigBoth<THelp> = CompletionConfigBase<THelp> & {
53
74
  /**
@@ -135,18 +156,46 @@ interface RunOptions<THelp, TError> {
135
156
  * Determines how help is made available:
136
157
  *
137
158
  * - `"command"`: Only the `help` subcommand is available
138
- * - `"option"`: Only the `--help` option is available
139
159
  * - `"both"`: Both `help` subcommand and `--help` option are available
140
160
  *
141
161
  * @default `"option"`
142
162
  */
143
- readonly mode?: "command" | "option" | "both";
163
+ readonly mode: "command" | "both";
164
+ /**
165
+ * Group label for the help command in help output. When specified,
166
+ * the help command appears under a titled section with this name
167
+ * instead of alongside user-defined commands.
168
+ *
169
+ * @since 0.10.0
170
+ */
171
+ readonly group?: string;
144
172
  /**
145
173
  * Callback function invoked when help is requested. The function can
146
174
  * optionally receive an exit code parameter.
147
175
  *
148
- * You usually want to pass `process.exit` on Node.js or Bun and `Deno.exit`
149
- * on Deno to this option.
176
+ * You usually want to pass `process.exit` on Node.js or Bun and
177
+ * `Deno.exit` on Deno to this option.
178
+ *
179
+ * @default Returns `void` when help is shown.
180
+ */
181
+ readonly onShow?: (() => THelp) | ((exitCode: number) => THelp);
182
+ } | {
183
+ /**
184
+ * Determines how help is made available:
185
+ *
186
+ * - `"option"`: Only the `--help` option is available
187
+ *
188
+ * @default `"option"`
189
+ */
190
+ readonly mode?: "option";
191
+ /** @since 0.10.0 */
192
+ readonly group?: never;
193
+ /**
194
+ * Callback function invoked when help is requested. The function can
195
+ * optionally receive an exit code parameter.
196
+ *
197
+ * You usually want to pass `process.exit` on Node.js or Bun and
198
+ * `Deno.exit` on Deno to this option.
150
199
  *
151
200
  * @default Returns `void` when help is shown.
152
201
  */
@@ -160,22 +209,55 @@ interface RunOptions<THelp, TError> {
160
209
  * Determines how version is made available:
161
210
  *
162
211
  * - `"command"`: Only the `version` subcommand is available
212
+ * - `"both"`: Both `version` subcommand and `--version` option are
213
+ * available
214
+ *
215
+ * @default `"option"`
216
+ */
217
+ readonly mode: "command" | "both";
218
+ /**
219
+ * The version string to display when version is requested.
220
+ */
221
+ readonly value: string;
222
+ /**
223
+ * Group label for the version command in help output. When specified,
224
+ * the version command appears under a titled section with this name
225
+ * instead of alongside user-defined commands.
226
+ *
227
+ * @since 0.10.0
228
+ */
229
+ readonly group?: string;
230
+ /**
231
+ * Callback function invoked when version is requested. The function can
232
+ * optionally receive an exit code parameter.
233
+ *
234
+ * You usually want to pass `process.exit` on Node.js or Bun and
235
+ * `Deno.exit` on Deno to this option.
236
+ *
237
+ * @default Returns `void` when version is shown.
238
+ */
239
+ readonly onShow?: (() => THelp) | ((exitCode: number) => THelp);
240
+ } | {
241
+ /**
242
+ * Determines how version is made available:
243
+ *
163
244
  * - `"option"`: Only the `--version` option is available
164
- * - `"both"`: Both `version` subcommand and `--version` option are available
165
245
  *
166
246
  * @default `"option"`
167
247
  */
168
- readonly mode?: "command" | "option" | "both";
248
+ readonly mode?: "option";
169
249
  /**
170
250
  * The version string to display when version is requested.
171
251
  */
172
252
  readonly value: string;
253
+ /** @since 0.10.0 */
254
+ readonly group?: never;
173
255
  /**
174
256
  * Callback function invoked when version is requested. The function can
175
257
  * optionally receive an exit code parameter.
176
258
  *
177
- * You usually want to pass `process.exit` on Node.js or Bun and `Deno.exit`
178
- * on Deno to this option.
259
+ * You usually want to pass `process.exit` on Node.js or Bun and
260
+ * `Deno.exit` on Deno to this option.
179
261
  *
180
262
  * @default Returns `void` when version is shown.
181
263
  */
package/dist/facade.d.ts CHANGED
@@ -24,30 +24,51 @@ type CompletionConfigBase<THelp> = {
24
24
  * Determines how completion is made available:
25
25
  *
26
26
  * - `"command"`: Only the `completion` subcommand is available
27
- * - `"option"`: Only the `--completion` option is available
28
- * - `"both"`: Both `completion` subcommand and `--completion` option are available
27
+ * - `"both"`: Both `completion` subcommand and `--completion` option
28
+ * are available
29
29
  *
30
30
  * @default `"both"`
31
31
  */
32
- readonly mode?: "command" | "option" | "both";
32
+ readonly mode?: "command" | "both";
33
+ /**
34
+ * Group label for the completion command in help output. When specified,
35
+ * the completion command appears under a titled section with this name
36
+ * instead of alongside user-defined commands.
37
+ *
38
+ * @since 0.10.0
39
+ */
40
+ readonly group?: string;
33
41
  /**
34
42
  * Available shell completions. By default, includes `bash`, `fish`, `nu`,
35
- * `pwsh`, and `zsh`. You can provide additional custom shell completions or
36
- * override the defaults.
43
+ * `pwsh`, and `zsh`. You can provide additional custom shell completions
44
+ * or override the defaults.
37
45
  *
38
46
  * @default `{ bash, fish, nu, pwsh, zsh }`
39
47
  */
40
48
  readonly shells?: Record<string, ShellCompletion>;
41
49
  /**
42
- * Callback function invoked when completion is requested. The function can
43
- * optionally receive an exit code parameter.
50
+ * Callback function invoked when completion is requested. The function
51
+ * can optionally receive an exit code parameter.
44
52
  *
45
- * You usually want to pass `process.exit` on Node.js or Bun and `Deno.exit`
46
- * on Deno to this option.
53
+ * You usually want to pass `process.exit` on Node.js or Bun and
54
+ * `Deno.exit` on Deno to this option.
47
55
  *
48
56
  * @default Returns `void` when completion is shown.
49
57
  */
50
58
  readonly onShow?: (() => THelp) | ((exitCode: number) => THelp);
59
+ } | {
60
+ /**
61
+ * Determines how completion is made available:
62
+ *
63
+ * - `"option"`: Only the `--completion` option is available
64
+ */
65
+ readonly mode: "option";
66
+ /** @since 0.10.0 */
67
+ readonly group?: never;
68
+ /** @default `{ bash, fish, nu, pwsh, zsh }` */
69
+ readonly shells?: Record<string, ShellCompletion>;
70
+ /** @default Returns `void` when completion is shown. */
71
+ readonly onShow?: (() => THelp) | ((exitCode: number) => THelp);
51
72
  };
52
73
  type CompletionConfigBoth<THelp> = CompletionConfigBase<THelp> & {
53
74
  /**
@@ -135,18 +156,46 @@ interface RunOptions<THelp, TError> {
135
156
  * Determines how help is made available:
136
157
  *
137
158
  * - `"command"`: Only the `help` subcommand is available
138
- * - `"option"`: Only the `--help` option is available
139
159
  * - `"both"`: Both `help` subcommand and `--help` option are available
140
160
  *
141
161
  * @default `"option"`
142
162
  */
143
- readonly mode?: "command" | "option" | "both";
163
+ readonly mode: "command" | "both";
164
+ /**
165
+ * Group label for the help command in help output. When specified,
166
+ * the help command appears under a titled section with this name
167
+ * instead of alongside user-defined commands.
168
+ *
169
+ * @since 0.10.0
170
+ */
171
+ readonly group?: string;
144
172
  /**
145
173
  * Callback function invoked when help is requested. The function can
146
174
  * optionally receive an exit code parameter.
147
175
  *
148
- * You usually want to pass `process.exit` on Node.js or Bun and `Deno.exit`
149
- * on Deno to this option.
176
+ * You usually want to pass `process.exit` on Node.js or Bun and
177
+ * `Deno.exit` on Deno to this option.
178
+ *
179
+ * @default Returns `void` when help is shown.
180
+ */
181
+ readonly onShow?: (() => THelp) | ((exitCode: number) => THelp);
182
+ } | {
183
+ /**
184
+ * Determines how help is made available:
185
+ *
186
+ * - `"option"`: Only the `--help` option is available
187
+ *
188
+ * @default `"option"`
189
+ */
190
+ readonly mode?: "option";
191
+ /** @since 0.10.0 */
192
+ readonly group?: never;
193
+ /**
194
+ * Callback function invoked when help is requested. The function can
195
+ * optionally receive an exit code parameter.
196
+ *
197
+ * You usually want to pass `process.exit` on Node.js or Bun and
198
+ * `Deno.exit` on Deno to this option.
150
199
  *
151
200
  * @default Returns `void` when help is shown.
152
201
  */
@@ -160,22 +209,55 @@ interface RunOptions<THelp, TError> {
160
209
  * Determines how version is made available:
161
210
  *
162
211
  * - `"command"`: Only the `version` subcommand is available
212
+ * - `"both"`: Both `version` subcommand and `--version` option are
213
+ * available
214
+ *
215
+ * @default `"option"`
216
+ */
217
+ readonly mode: "command" | "both";
218
+ /**
219
+ * The version string to display when version is requested.
220
+ */
221
+ readonly value: string;
222
+ /**
223
+ * Group label for the version command in help output. When specified,
224
+ * the version command appears under a titled section with this name
225
+ * instead of alongside user-defined commands.
226
+ *
227
+ * @since 0.10.0
228
+ */
229
+ readonly group?: string;
230
+ /**
231
+ * Callback function invoked when version is requested. The function can
232
+ * optionally receive an exit code parameter.
233
+ *
234
+ * You usually want to pass `process.exit` on Node.js or Bun and
235
+ * `Deno.exit` on Deno to this option.
236
+ *
237
+ * @default Returns `void` when version is shown.
238
+ */
239
+ readonly onShow?: (() => THelp) | ((exitCode: number) => THelp);
240
+ } | {
241
+ /**
242
+ * Determines how version is made available:
243
+ *
163
244
  * - `"option"`: Only the `--version` option is available
164
- * - `"both"`: Both `version` subcommand and `--version` option are available
165
245
  *
166
246
  * @default `"option"`
167
247
  */
168
- readonly mode?: "command" | "option" | "both";
248
+ readonly mode?: "option";
169
249
  /**
170
250
  * The version string to display when version is requested.
171
251
  */
172
252
  readonly value: string;
253
+ /** @since 0.10.0 */
254
+ readonly group?: never;
173
255
  /**
174
256
  * Callback function invoked when version is requested. The function can
175
257
  * optionally receive an exit code parameter.
176
258
  *
177
- * You usually want to pass `process.exit` on Node.js or Bun and `Deno.exit`
178
- * on Deno to this option.
259
+ * You usually want to pass `process.exit` on Node.js or Bun and
260
+ * `Deno.exit` on Deno to this option.
179
261
  *
180
262
  * @default Returns `void` when version is shown.
181
263
  */
package/dist/facade.js CHANGED
@@ -2,7 +2,7 @@ import { annotationKey } from "./annotations.js";
2
2
  import { commandLine, formatMessage, lineBreak, message, optionName, text, value } from "./message.js";
3
3
  import { bash, fish, nu, pwsh, zsh } from "./completion.js";
4
4
  import { formatUsage } from "./usage.js";
5
- import { longestMatch, object } from "./constructs.js";
5
+ import { group, longestMatch, object } from "./constructs.js";
6
6
  import { formatDocPage } from "./doc.js";
7
7
  import { multiple, optional, withDefault } from "./modifiers.js";
8
8
  import { string } from "./valueparser.js";
@@ -113,10 +113,7 @@ function createCompletionParser(mode, programName, availableShells, name = "both
113
113
  };
114
114
  }
115
115
  }
116
- /**
117
- * Systematically combines the original parser with help, version, and completion parsers.
118
- */
119
- function combineWithHelpVersion(originalParser, helpParsers, versionParsers, completionParsers) {
116
+ function combineWithHelpVersion(originalParser, helpParsers, versionParsers, completionParsers, groups) {
120
117
  const parsers = [];
121
118
  if (helpParsers.helpOption) {
122
119
  const lenientHelpParser = {
@@ -262,26 +259,35 @@ function combineWithHelpVersion(originalParser, helpParsers, versionParsers, com
262
259
  };
263
260
  parsers.push(lenientVersionParser);
264
261
  }
265
- if (versionParsers.versionCommand) parsers.push(object({
266
- help: constant(false),
267
- version: constant(true),
268
- completion: constant(false),
269
- result: versionParsers.versionCommand,
270
- helpFlag: helpParsers.helpOption ? optional(helpParsers.helpOption) : constant(false)
271
- }));
272
- if (completionParsers.completionCommand) parsers.push(object({
273
- help: constant(false),
274
- version: constant(false),
275
- completion: constant(true),
276
- completionData: completionParsers.completionCommand,
277
- helpFlag: helpParsers.helpOption ? optional(helpParsers.helpOption) : constant(false)
278
- }));
279
- if (helpParsers.helpCommand) parsers.push(object({
280
- help: constant(true),
281
- version: constant(false),
282
- completion: constant(false),
283
- commands: helpParsers.helpCommand
284
- }));
262
+ if (versionParsers.versionCommand) {
263
+ const versionParser = object({
264
+ help: constant(false),
265
+ version: constant(true),
266
+ completion: constant(false),
267
+ result: versionParsers.versionCommand,
268
+ helpFlag: helpParsers.helpOption ? optional(helpParsers.helpOption) : constant(false)
269
+ });
270
+ parsers.push(groups?.versionGroup ? group(groups.versionGroup, versionParser) : versionParser);
271
+ }
272
+ if (completionParsers.completionCommand) {
273
+ const completionParser = object({
274
+ help: constant(false),
275
+ version: constant(false),
276
+ completion: constant(true),
277
+ completionData: completionParsers.completionCommand,
278
+ helpFlag: helpParsers.helpOption ? optional(helpParsers.helpOption) : constant(false)
279
+ });
280
+ parsers.push(groups?.completionGroup ? group(groups.completionGroup, completionParser) : completionParser);
281
+ }
282
+ if (helpParsers.helpCommand) {
283
+ const helpParser = object({
284
+ help: constant(true),
285
+ version: constant(false),
286
+ completion: constant(false),
287
+ commands: helpParsers.helpCommand
288
+ });
289
+ parsers.push(groups?.helpGroup ? group(groups.helpGroup, helpParser) : helpParser);
290
+ }
285
291
  parsers.push(object({
286
292
  help: constant(false),
287
293
  version: constant(false),
@@ -456,13 +462,16 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
456
462
  }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
457
463
  const helpMode = options.help?.mode ?? "option";
458
464
  const onHelp = options.help?.onShow ?? (() => ({}));
465
+ const helpGroup = options.help?.group;
459
466
  const versionMode = options.version?.mode ?? "option";
460
467
  const versionValue = options.version?.value ?? "";
461
468
  const onVersion = options.version?.onShow ?? (() => ({}));
469
+ const versionGroup = options.version?.group;
462
470
  const completionMode = options.completion?.mode ?? "both";
463
471
  const completionName = options.completion?.name ?? "both";
464
472
  const completionHelpVisibility = options.completion?.helpVisibility ?? completionName;
465
473
  const onCompletion = options.completion?.onShow ?? (() => ({}));
474
+ const completionGroup = options.completion?.group;
466
475
  const defaultShells = {
467
476
  bash,
468
477
  fish,
@@ -511,7 +520,11 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
511
520
  }
512
521
  }
513
522
  }
514
- const augmentedParser = help === "none" && version === "none" && completion === "none" ? parser : combineWithHelpVersion(parser, helpParsers, versionParsers, completionParsers);
523
+ const augmentedParser = help === "none" && version === "none" && completion === "none" ? parser : combineWithHelpVersion(parser, helpParsers, versionParsers, completionParsers, {
524
+ helpGroup,
525
+ versionGroup,
526
+ completionGroup
527
+ });
515
528
  const handleResult = (result) => {
516
529
  const classified = classifyResult(result, args);
517
530
  switch (classified.type) {
@@ -535,15 +548,18 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
535
548
  else if (requestedCommand === "version" && versionAsCommand && versionParsers.versionCommand) helpGeneratorParser = versionParsers.versionCommand;
536
549
  else {
537
550
  const commandParsers = [parser];
538
- if (helpAsCommand) {
539
- if (helpParsers.helpCommand) commandParsers.push(helpParsers.helpCommand);
540
- }
541
- if (versionAsCommand) {
542
- if (versionParsers.versionCommand) commandParsers.push(versionParsers.versionCommand);
543
- }
544
- if (completionAsCommand) {
545
- if (completionParsers.completionCommand) commandParsers.push(completionParsers.completionCommand);
546
- }
551
+ const groupedMeta = {};
552
+ const ungroupedMeta = [];
553
+ const addMeta = (p, groupLabel) => {
554
+ if (groupLabel) (groupedMeta[groupLabel] ??= []).push(p);
555
+ else ungroupedMeta.push(p);
556
+ };
557
+ if (helpAsCommand && helpParsers.helpCommand) addMeta(helpParsers.helpCommand, helpGroup);
558
+ if (versionAsCommand && versionParsers.versionCommand) addMeta(versionParsers.versionCommand, versionGroup);
559
+ if (completionAsCommand && completionParsers.completionCommand) addMeta(completionParsers.completionCommand, completionGroup);
560
+ commandParsers.push(...ungroupedMeta);
561
+ for (const [label, parsers] of Object.entries(groupedMeta)) if (parsers.length === 1) commandParsers.push(group(label, parsers[0]));
562
+ else commandParsers.push(group(label, longestMatch(...parsers)));
547
563
  if (commandParsers.length === 1) helpGeneratorParser = commandParsers[0];
548
564
  else if (commandParsers.length === 2) helpGeneratorParser = longestMatch(commandParsers[0], commandParsers[1]);
549
565
  else helpGeneratorParser = longestMatch(...commandParsers);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.10.0-dev.374+981b7fab",
3
+ "version": "0.10.0-dev.375+fffd225e",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",