@optique/run 0.10.7 → 1.0.0-dev.1116

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/run.d.ts CHANGED
@@ -1,8 +1,10 @@
1
+ import { CommandSubConfig, ContextOptionsParam, OptionSubConfig } from "@optique/core/facade";
1
2
  import { Message } from "@optique/core/message";
2
3
  import { ShellCompletion } from "@optique/core/completion";
4
+ import { SourceContext } from "@optique/core/context";
3
5
  import { InferMode, InferValue, Mode, ModeValue, Parser } from "@optique/core/parser";
4
6
  import { Program } from "@optique/core/program";
5
- import { ShowChoicesOptions, ShowDefaultOptions } from "@optique/core/doc";
7
+ import { DocSection, ShowChoicesOptions, ShowDefaultOptions } from "@optique/core/doc";
6
8
 
7
9
  //#region src/run.d.ts
8
10
 
@@ -22,6 +24,26 @@ interface RunOptions {
22
24
  * @default `process.argv.slice(2)` (arguments after the script name)
23
25
  */
24
26
  readonly args?: readonly string[];
27
+ /**
28
+ * Function used to output help and usage messages. Assumes it prints the
29
+ * ending newline.
30
+ *
31
+ * @default Writes to `process.stdout` with a trailing newline
32
+ */
33
+ readonly stdout?: (text: string) => void;
34
+ /**
35
+ * Function used to output error messages. Assumes it prints the ending
36
+ * newline.
37
+ *
38
+ * @default Writes to `process.stderr` with a trailing newline
39
+ */
40
+ readonly stderr?: (text: string) => void;
41
+ /**
42
+ * Function used to exit the process on help/version display or parse error.
43
+ *
44
+ * @default `process.exit`
45
+ */
46
+ readonly onExit?: (exitCode: number) => never;
25
47
  /**
26
48
  * Whether to enable colored output in help and error messages.
27
49
  *
@@ -62,69 +84,86 @@ interface RunOptions {
62
84
  * @since 0.10.0
63
85
  */
64
86
  readonly showChoices?: boolean | ShowChoicesOptions;
87
+ /**
88
+ * A custom comparator function to control the order of sections in the
89
+ * help output. When provided, it is used instead of the default smart
90
+ * sort (command-only sections first, then mixed, then option/argument-only
91
+ * sections). Sections that compare equal (return `0`) preserve their
92
+ * original relative order.
93
+ *
94
+ * @param a The first section to compare.
95
+ * @param b The second section to compare.
96
+ * @returns A negative number if `a` should appear before `b`, a positive
97
+ * number if `a` should appear after `b`, or `0` if they are equal.
98
+ * @since 1.0.0
99
+ */
100
+ readonly sectionOrder?: (a: DocSection, b: DocSection) => number;
65
101
  /**
66
102
  * Help configuration. Determines how help is made available:
67
103
  *
68
104
  * - `"command"`: Only the `help` subcommand is available
69
105
  * - `"option"`: Only the `--help` option is available
70
106
  * - `"both"`: Both `help` subcommand and `--help` option are available
71
- * - `object`: Advanced configuration with mode and group
72
- * - `mode`: "command" | "both"
73
- * - `group`: Group label for help command in help output (optional)
107
+ * - `object`: Advanced configuration with `command` and/or `option`
108
+ * sub-configs. At least one of `command` or `option` must be specified.
74
109
  *
75
110
  * When not provided, help functionality is disabled.
111
+ *
112
+ * @since 1.0.0
76
113
  */
77
- readonly help?: "command" | "option" | "both" | {
78
- readonly mode: "command" | "both";
79
- /**
80
- * Group label for the help command in help output.
81
- * @since 0.10.0
82
- */
83
- readonly group?: string;
84
- };
114
+ readonly help?: "command" | "option" | "both" | (({
115
+ readonly command: true | CommandSubConfig;
116
+ readonly option?: true | OptionSubConfig;
117
+ } | {
118
+ readonly option: true | OptionSubConfig;
119
+ readonly command?: true | CommandSubConfig;
120
+ }));
85
121
  /**
86
122
  * Version configuration. Determines how version is made available:
87
123
  *
88
124
  * - `string`: Version value with default `"option"` mode (--version only)
89
- * - `object`: Advanced configuration with version value and mode
90
- * - `value`: The version string to display
91
- * - `mode`: "command" | "option" | "both" (default: "option")
92
- * - `group`: Group label for version command in help output (only
93
- * when mode is "command" or "both")
125
+ * - `object`: Advanced configuration with version value and `command`
126
+ * and/or `option` sub-configs. At least one of `command` or `option`
127
+ * must be specified.
94
128
  *
95
129
  * When not provided, version functionality is disabled.
130
+ *
131
+ * @since 1.0.0
96
132
  */
97
- readonly version?: string | {
133
+ readonly version?: string | ({
98
134
  readonly value: string;
99
- readonly mode: "command" | "both";
100
- /**
101
- * Group label for the version command in help output.
102
- * @since 0.10.0
103
- */
104
- readonly group?: string;
135
+ } & ({
136
+ readonly command: true | CommandSubConfig;
137
+ readonly option?: true | OptionSubConfig;
105
138
  } | {
106
- readonly value: string;
107
- readonly mode?: "option";
108
- readonly group?: never;
109
- };
139
+ readonly option: true | OptionSubConfig;
140
+ readonly command?: true | CommandSubConfig;
141
+ }));
110
142
  /**
111
- * Completion configuration. Determines how shell completion is made available:
143
+ * Completion configuration. Determines how shell completion is made
144
+ * available:
112
145
  *
113
146
  * - `"command"`: Only the `completion` subcommand is available
114
147
  * - `"option"`: Only the `--completion` option is available
115
- * - `"both"`: Both `completion` subcommand and `--completion` option are available
116
- * - `object`: Advanced configuration with mode and custom shells
117
- * - `mode`: "command" | "option" | "both" (default: "both")
118
- * - `name`: "singular" | "plural" | "both" (default: "both")
119
- * - `helpVisibility`: "singular" | "plural" | "both" | "none"
120
- * (default: matches `name`)
121
- * - `shells`: Custom shell completions (optional)
148
+ * - `"both"`: Both `completion` subcommand and `--completion` option
149
+ * are available
150
+ * - `object`: Advanced configuration with `command` and/or `option`
151
+ * sub-configs and optional custom shells. At least one of `command`
152
+ * or `option` must be specified.
122
153
  *
123
154
  * When not provided, completion functionality is disabled.
124
155
  *
125
- * @since 0.6.0
156
+ * @since 1.0.0
126
157
  */
127
- readonly completion?: "command" | "option" | "both" | CompletionOptions;
158
+ readonly completion?: "command" | "option" | "both" | ({
159
+ readonly shells?: Record<string, ShellCompletion>;
160
+ } & ({
161
+ readonly command: true | CommandSubConfig;
162
+ readonly option?: true | OptionSubConfig;
163
+ } | {
164
+ readonly option: true | OptionSubConfig;
165
+ readonly command?: true | CommandSubConfig;
166
+ }));
128
167
  /**
129
168
  * What to display above error messages:
130
169
  *
@@ -177,49 +216,59 @@ interface RunOptions {
177
216
  * @since 0.4.0
178
217
  */
179
218
  readonly footer?: Message;
180
- }
181
- type CompletionHelpVisibility = "singular" | "plural" | "both" | "none";
182
- type CompletionOptionsBase = {
183
- readonly mode?: "command" | "both";
184
- /**
185
- * Group label for the completion command in help output.
186
- * @since 0.10.0
187
- */
188
- readonly group?: string;
189
- readonly shells?: Record<string, ShellCompletion>;
190
- } | {
191
- readonly mode: "option";
192
- readonly group?: never;
193
- readonly shells?: Record<string, ShellCompletion>;
194
- };
195
- type CompletionOptionsBoth = CompletionOptionsBase & {
196
- readonly name?: "both";
197
- /**
198
- * Controls which completion aliases are shown in help and usage output.
199
- *
200
- * @since 0.10.0
201
- */
202
- readonly helpVisibility?: CompletionHelpVisibility;
203
- };
204
- type CompletionOptionsSingular = CompletionOptionsBase & {
205
- readonly name: "singular";
206
219
  /**
207
- * Controls which completion aliases are shown in help and usage output.
220
+ * Source contexts to use for two-phase parsing. When provided, the
221
+ * runner delegates to `runWith()` (or `runWithSync()` for sync parsers)
222
+ * from `@optique/core/facade`, which handles annotation collection and
223
+ * multi-phase parsing automatically.
208
224
  *
209
- * @since 0.10.0
225
+ * @since 1.0.0
210
226
  */
211
- readonly helpVisibility?: "singular" | "none";
212
- };
213
- type CompletionOptionsPlural = CompletionOptionsBase & {
214
- readonly name: "plural";
227
+ readonly contexts?: readonly SourceContext<unknown>[];
215
228
  /**
216
- * Controls which completion aliases are shown in help and usage output.
229
+ * Options to forward to source contexts. When contexts declare
230
+ * required options (via `$requiredOptions`), pass them here to
231
+ * avoid name collisions with runner-level options such as `help`,
232
+ * `programName`, or `version`.
217
233
  *
218
- * @since 0.10.0
234
+ * @since 1.0.0
219
235
  */
220
- readonly helpVisibility?: "plural" | "none";
221
- };
222
- type CompletionOptions = CompletionOptionsBoth | CompletionOptionsSingular | CompletionOptionsPlural;
236
+ readonly contextOptions?: Record<string, unknown>;
237
+ }
238
+ /**
239
+ * Rejects context tuples that are statically known to be empty so those calls
240
+ * fall back to the no-context overloads.
241
+ */
242
+ type RejectEmptyContexts<TContexts extends readonly SourceContext<unknown>[]> = TContexts extends readonly [] ? never : unknown;
243
+ /**
244
+ * Represents a context tuple that is statically known to contain at least one
245
+ * source context.
246
+ */
247
+ type NonEmptySourceContexts = readonly [SourceContext<unknown>, ...SourceContext<unknown>[]];
248
+ /**
249
+ * Rejects option shapes that may carry a non-empty `contexts` array so plain
250
+ * Program overloads do not bypass the context-aware overloads.
251
+ */
252
+ type ContextsFromOptions<TOptions> = [Exclude<TOptions, undefined>] extends [never] ? undefined : Exclude<TOptions, undefined> extends {
253
+ readonly contexts?: infer TContexts extends readonly SourceContext<unknown>[] | undefined;
254
+ } ? TContexts : undefined;
255
+ type RejectContextfulOptions<TOptions> = [ContextsFromOptions<TOptions>] extends [undefined | readonly []] ? unknown : never;
256
+ /**
257
+ * Rejects option shapes that introduce keys outside the public `RunOptions`
258
+ * contract, preserving typo detection for direct object literals and wider
259
+ * option variables.
260
+ */
261
+ type RejectUnknownRunOptionKeys<TOptions> = [TOptions] extends [undefined] ? unknown : Exclude<keyof TOptions, keyof RunOptions> extends never ? unknown : never;
262
+ /**
263
+ * Accepts only values typed exactly as `RunOptions`, which are widened to the
264
+ * conservative fallback overloads because they may hide context presence.
265
+ */
266
+ type AcceptExactRunOptions<TOptions> = [TOptions] extends [RunOptions] ? [RunOptions] extends [TOptions] ? unknown : never : never;
267
+ /**
268
+ * Accepts only values typed exactly as `RunOptions | undefined`, which model
269
+ * optional forwarding wrappers without widening generic Program overloads.
270
+ */
271
+ type AcceptExactOptionalRunOptions<TOptions> = [TOptions] extends [RunOptions | undefined] ? [RunOptions | undefined] extends [TOptions] ? unknown : never : never;
223
272
  /**
224
273
  * Runs a command-line parser with automatic process integration.
225
274
  *
@@ -285,8 +334,25 @@ type CompletionOptions = CompletionOptionsBoth | CompletionOptionsSingular | Com
285
334
  *
286
335
  * @since 0.11.0 Added support for {@link Program} objects.
287
336
  */
288
- declare function run<T>(program: Program<"sync", T>, options?: RunOptions): T;
289
- declare function run<T>(program: Program<"async", T>, options?: RunOptions): Promise<T>;
337
+ declare function run<T extends Parser<Mode, unknown, unknown>, const TContexts extends NonEmptySourceContexts>(parser: T, options: RunOptions & {
338
+ readonly contexts: TContexts;
339
+ } & ContextOptionsParam<TContexts, InferValue<T>>): Promise<InferValue<T>>;
340
+ declare function run<T extends Parser<Mode, unknown, unknown>, const TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
341
+ readonly contexts: TContexts;
342
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, InferValue<T>>): ModeValue<InferMode<T>, InferValue<T>> | Promise<InferValue<T>>;
343
+ declare function run<M extends Mode, T, const TContexts extends NonEmptySourceContexts>(program: Program<M, T>, options: RunOptions & {
344
+ readonly contexts: TContexts;
345
+ } & ContextOptionsParam<TContexts, T>): Promise<T>;
346
+ declare function run<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"sync", T>, options: RunOptions & {
347
+ readonly contexts: TContexts;
348
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): T | Promise<T>;
349
+ declare function run<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"async", T>, options: RunOptions & {
350
+ readonly contexts: TContexts;
351
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): Promise<T>;
352
+ declare function run<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): T;
353
+ declare function run<T, TOptions extends RunOptions>(program: Program<"sync", T>, options: TOptions & AcceptExactRunOptions<TOptions>): T | Promise<T>;
354
+ declare function run<T, const TOptions extends RunOptions | undefined>(program: Program<"async", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
355
+ declare function run<T, TOptions extends RunOptions>(program: Program<"async", T>, options: TOptions & AcceptExactRunOptions<TOptions>): Promise<T>;
290
356
  declare function run<T extends Parser<"sync", unknown, unknown>>(parser: T, options?: RunOptions): InferValue<T>;
291
357
  declare function run<T extends Parser<"async", unknown, unknown>>(parser: T, options?: RunOptions): Promise<InferValue<T>>;
292
358
  declare function run<T extends Parser<Mode, unknown, unknown>>(parser: T, options?: RunOptions): ModeValue<InferMode<T>, InferValue<T>>;
@@ -303,7 +369,15 @@ declare function run<T extends Parser<Mode, unknown, unknown>>(parser: T, option
303
369
  * @returns The parsed result if successful.
304
370
  * @since 0.9.0
305
371
  */
306
- declare function runSync<T>(program: Program<"sync", T>, options?: RunOptions): T;
372
+ declare function runSync<T extends Parser<"sync", unknown, unknown>, TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
373
+ readonly contexts: TContexts;
374
+ } & ContextOptionsParam<TContexts, InferValue<T>>): InferValue<T>;
375
+ declare function runSync<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"sync", T>, options: RunOptions & {
376
+ readonly contexts: TContexts;
377
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): T;
378
+ declare function runSync<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): T;
379
+ declare function runSync<T, TOptions extends RunOptions>(program: Program<"sync", T>, options: TOptions & AcceptExactRunOptions<TOptions>): T;
380
+ declare function runSync<T, TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options: TOptions & AcceptExactOptionalRunOptions<TOptions>): T;
307
381
  declare function runSync<T extends Parser<"sync", unknown, unknown>>(parser: T, options?: RunOptions): InferValue<T>;
308
382
  /**
309
383
  * Runs an asynchronous command-line parser with automatic process integration.
@@ -318,8 +392,16 @@ declare function runSync<T extends Parser<"sync", unknown, unknown>>(parser: T,
318
392
  * @returns A Promise of the parsed result if successful.
319
393
  * @since 0.9.0
320
394
  */
321
- declare function runAsync<T>(program: Program<"sync", T>, options?: RunOptions): Promise<T>;
322
- declare function runAsync<T>(program: Program<"async", T>, options?: RunOptions): Promise<T>;
395
+ declare function runAsync<T extends Parser<Mode, unknown, unknown>, TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
396
+ readonly contexts: TContexts;
397
+ } & ContextOptionsParam<TContexts, InferValue<T>>): Promise<InferValue<T>>;
398
+ declare function runAsync<M extends Mode, T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<M, T>, options: RunOptions & {
399
+ readonly contexts: TContexts;
400
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): Promise<T>;
401
+ declare function runAsync<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
402
+ declare function runAsync<T, const TOptions extends RunOptions | undefined>(program: Program<"async", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
403
+ declare function runAsync<T, TOptions extends RunOptions>(program: Program<Mode, T>, options: TOptions & AcceptExactRunOptions<TOptions>): Promise<T>;
404
+ declare function runAsync<T, TOptions extends RunOptions | undefined>(program: Program<Mode, T>, options: TOptions & AcceptExactOptionalRunOptions<TOptions>): Promise<T>;
323
405
  declare function runAsync<T extends Parser<Mode, unknown, unknown>>(parser: T, options?: RunOptions): Promise<InferValue<T>>;
324
406
  //#endregion
325
407
  export { RunOptions, run, runAsync, runSync };
package/dist/run.js CHANGED
@@ -1,113 +1,155 @@
1
- import { runParser } from "@optique/core/facade";
1
+ import { runParser, runWith, runWithSync } from "@optique/core/facade";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
4
 
5
5
  //#region src/run.ts
6
+ function getProgramHelpMetadata(metadata) {
7
+ return {
8
+ brief: metadata.brief,
9
+ description: metadata.description,
10
+ examples: metadata.examples,
11
+ author: metadata.author,
12
+ bugs: metadata.bugs,
13
+ footer: metadata.footer
14
+ };
15
+ }
16
+ function resolveProgramInput(parserOrProgram, options) {
17
+ if ("parser" in parserOrProgram && "metadata" in parserOrProgram) return {
18
+ parser: parserOrProgram.parser,
19
+ options: options.programName == null ? {
20
+ ...options,
21
+ programName: parserOrProgram.metadata.name
22
+ } : options,
23
+ programMetadata: getProgramHelpMetadata(parserOrProgram.metadata)
24
+ };
25
+ return {
26
+ parser: parserOrProgram,
27
+ options
28
+ };
29
+ }
6
30
  function run(parserOrProgram, options = {}) {
7
31
  return runImpl(parserOrProgram, options);
8
32
  }
9
33
  function runSync(parserOrProgram, options = {}) {
34
+ const contexts = options.contexts;
35
+ if (contexts && contexts.length > 0) {
36
+ const resolved = resolveProgramInput(parserOrProgram, options);
37
+ const { parser, programMetadata } = resolved;
38
+ options = resolved.options;
39
+ const { programName, args, coreOptions } = buildCoreOptions(options, programMetadata);
40
+ const runWithOptions = {
41
+ ...coreOptions,
42
+ contextOptions: options.contextOptions,
43
+ args
44
+ };
45
+ return runWithSync(parser, programName, contexts, runWithOptions);
46
+ }
10
47
  return runImpl(parserOrProgram, options);
11
48
  }
12
49
  function runAsync(parserOrProgram, options = {}) {
13
50
  const result = runImpl(parserOrProgram, options);
14
51
  return Promise.resolve(result);
15
52
  }
16
- function runImpl(parserOrProgram, options = {}) {
17
- const isProgram = "parser" in parserOrProgram && "metadata" in parserOrProgram;
18
- let parser;
19
- let programNameFromProgram;
20
- let programMetadata;
21
- if (isProgram) {
22
- const program = parserOrProgram;
23
- parser = program.parser;
24
- programNameFromProgram = program.metadata.name;
25
- programMetadata = {
26
- brief: program.metadata.brief,
27
- description: program.metadata.description,
28
- examples: program.metadata.examples,
29
- author: program.metadata.author,
30
- bugs: program.metadata.bugs,
31
- footer: program.metadata.footer
53
+ /**
54
+ * Builds core run options from the simplified RunOptions format.
55
+ *
56
+ * Converts the user-friendly RunOptions (string-based help/version/completion)
57
+ * into the verbose CoreRunOptions format expected by `runParser()`, `runWith()`,
58
+ * and `runWithSync()` from `@optique/core/facade`.
59
+ *
60
+ * @internal
61
+ */
62
+ function buildCoreOptions(options, programMetadata) {
63
+ const programName = options.programName ?? path.basename(process.argv[1] ?? "cli");
64
+ const args = options.args ?? process.argv.slice(2);
65
+ const stdout = options.stdout ?? ((line) => {
66
+ process.stdout.write(`${line}\n`);
67
+ });
68
+ const stderr = options.stderr ?? ((line) => {
69
+ process.stderr.write(`${line}\n`);
70
+ });
71
+ const onExit = options.onExit ?? ((exitCode) => process.exit(exitCode));
72
+ const colors = options.colors ?? process.stdout.isTTY;
73
+ const maxWidth = options.maxWidth ?? process.stdout.columns;
74
+ const showDefault = options.showDefault;
75
+ const showChoices = options.showChoices;
76
+ const sectionOrder = options.sectionOrder;
77
+ const help = options.help;
78
+ const version = options.version;
79
+ const completion = options.completion;
80
+ const aboveError = options.aboveError ?? "usage";
81
+ const errorExitCode = options.errorExitCode ?? 1;
82
+ const brief = options.brief ?? programMetadata?.brief;
83
+ const description = options.description ?? programMetadata?.description;
84
+ const examples = options.examples ?? programMetadata?.examples;
85
+ const author = options.author ?? programMetadata?.author;
86
+ const bugs = options.bugs ?? programMetadata?.bugs;
87
+ const footer = options.footer ?? programMetadata?.footer;
88
+ const onShow = () => onExit(0);
89
+ const helpConfig = (() => {
90
+ if (!help) return void 0;
91
+ if (typeof help === "string") switch (help) {
92
+ case "command": return {
93
+ command: true,
94
+ onShow
95
+ };
96
+ case "option": return {
97
+ option: true,
98
+ onShow
99
+ };
100
+ case "both": return {
101
+ command: true,
102
+ option: true,
103
+ onShow
104
+ };
105
+ }
106
+ return {
107
+ ...help,
108
+ onShow
32
109
  };
33
- } else parser = parserOrProgram;
34
- const { programName = programNameFromProgram ?? path.basename(process.argv[1] || "cli"), args = process.argv.slice(2), colors = process.stdout.isTTY, maxWidth = process.stdout.columns, showDefault, showChoices, help, version, completion, aboveError = "usage", errorExitCode = 1, brief = programMetadata?.brief, description = programMetadata?.description, examples = programMetadata?.examples, author = programMetadata?.author, bugs = programMetadata?.bugs, footer = programMetadata?.footer } = options;
35
- const helpConfig = help ? typeof help === "string" ? {
36
- mode: help,
37
- onShow: () => process.exit(0)
38
- } : {
39
- mode: help.mode,
40
- group: help.group,
41
- onShow: () => process.exit(0)
42
- } : void 0;
110
+ })();
43
111
  const versionConfig = (() => {
44
112
  if (!version) return void 0;
45
113
  if (typeof version === "string") return {
46
- mode: "option",
47
114
  value: version,
48
- onShow: () => process.exit(0)
49
- };
50
- const mode = version.mode ?? "option";
51
- if (mode === "command" || mode === "both") return {
52
- mode,
53
- value: version.value,
54
- group: version.group,
55
- onShow: () => process.exit(0)
115
+ option: true,
116
+ onShow
56
117
  };
57
118
  return {
58
- mode,
59
- value: version.value,
60
- onShow: () => process.exit(0)
119
+ ...version,
120
+ onShow
61
121
  };
62
122
  })();
63
123
  const completionConfig = (() => {
64
124
  if (!completion) return void 0;
65
- const onShow = () => process.exit(0);
66
- if (typeof completion === "string") return {
67
- mode: completion,
68
- name: "both",
69
- helpVisibility: "both",
70
- onShow
71
- };
72
- const mode = completion.mode ?? "both";
73
- const shells = completion.shells;
74
- const cGroup = completion.group;
75
- if (completion.name === "singular") return {
76
- mode,
77
- shells,
78
- ...cGroup != null && { group: cGroup },
79
- name: "singular",
80
- helpVisibility: completion.helpVisibility ?? "singular",
81
- onShow
82
- };
83
- if (completion.name === "plural") return {
84
- mode,
85
- shells,
86
- ...cGroup != null && { group: cGroup },
87
- name: "plural",
88
- helpVisibility: completion.helpVisibility ?? "plural",
89
- onShow
90
- };
125
+ if (typeof completion === "string") switch (completion) {
126
+ case "command": return {
127
+ command: true,
128
+ onShow
129
+ };
130
+ case "option": return {
131
+ option: true,
132
+ onShow
133
+ };
134
+ case "both": return {
135
+ command: true,
136
+ option: true,
137
+ onShow
138
+ };
139
+ }
91
140
  return {
92
- mode,
93
- shells,
94
- ...cGroup != null && { group: cGroup },
95
- name: "both",
96
- helpVisibility: completion.helpVisibility ?? "both",
141
+ ...completion,
97
142
  onShow
98
143
  };
99
144
  })();
100
- return runParser(parser, programName, args, {
101
- stderr(line) {
102
- process.stderr.write(`${line}\n`);
103
- },
104
- stdout(line) {
105
- process.stdout.write(`${line}\n`);
106
- },
145
+ const coreOptions = {
146
+ stderr,
147
+ stdout,
107
148
  colors,
108
149
  maxWidth,
109
150
  showDefault,
110
151
  showChoices,
152
+ sectionOrder,
111
153
  help: helpConfig,
112
154
  version: versionConfig,
113
155
  completion: completionConfig,
@@ -119,9 +161,31 @@ function runImpl(parserOrProgram, options = {}) {
119
161
  bugs,
120
162
  footer,
121
163
  onError() {
122
- return process.exit(errorExitCode);
164
+ return onExit(errorExitCode);
123
165
  }
124
- });
166
+ };
167
+ return {
168
+ programName,
169
+ args,
170
+ coreOptions
171
+ };
172
+ }
173
+ function runImpl(parserOrProgram, options = {}) {
174
+ const resolved = resolveProgramInput(parserOrProgram, options);
175
+ const { parser, programMetadata } = resolved;
176
+ options = resolved.options;
177
+ const contexts = options.contexts;
178
+ if (contexts && contexts.length > 0) {
179
+ const { programName: programName$1, args: args$1, coreOptions: coreOptions$1 } = buildCoreOptions(options, programMetadata);
180
+ const runWithOptions = {
181
+ ...coreOptions$1,
182
+ contextOptions: options.contextOptions,
183
+ args: args$1
184
+ };
185
+ return runWith(parser, programName$1, contexts, runWithOptions);
186
+ }
187
+ const { programName, args, coreOptions } = buildCoreOptions(options, programMetadata);
188
+ return runParser(parser, programName, args, coreOptions);
125
189
  }
126
190
 
127
191
  //#endregion
@@ -14,6 +14,17 @@ const node_fs = require_rolldown_runtime.__toESM(require("node:fs"));
14
14
  *
15
15
  * @param options Configuration options for path validation.
16
16
  * @returns A ValueParser that validates and returns string paths.
17
+ * @throws {TypeError} If {@link PathOptionsBase.type} is not one of
18
+ * `"file"`, `"directory"`, or `"either"`.
19
+ * @throws {TypeError} If any entry in {@link PathOptionsBase.extensions} does
20
+ * not start with a dot (e.g., `"json"` instead of `".json"`).
21
+ * @throws {TypeError} If {@link PathOptionsMustExist.mustExist} is not a
22
+ * boolean.
23
+ * @throws {TypeError} If {@link PathOptionsMustNotExist.mustNotExist} is not a
24
+ * boolean.
25
+ * @throws {TypeError} If {@link PathOptionsBase.allowCreate} is not a boolean.
26
+ * @throws {TypeError} If both {@link PathOptionsMustExist.mustExist} and
27
+ * {@link PathOptionsMustNotExist.mustNotExist} are `true`.
17
28
  *
18
29
  * @example
19
30
  * ```typescript
@@ -43,16 +54,30 @@ const node_fs = require_rolldown_runtime.__toESM(require("node:fs"));
43
54
  function path(options = {}) {
44
55
  const { metavar = "PATH", type = "either", allowCreate = false, extensions } = options;
45
56
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
57
+ if (type !== "file" && type !== "directory" && type !== "either") throw new TypeError(`Unsupported path type: ${JSON.stringify(type)}. Expected "file", "directory", or "either".`);
58
+ if (extensions) {
59
+ for (const ext of extensions) if (!ext.startsWith(".")) throw new TypeError(`Each extension must start with a dot, got: ${JSON.stringify(ext)}`);
60
+ }
61
+ if (options.allowCreate !== void 0 && typeof options.allowCreate !== "boolean") throw new TypeError(`Expected allowCreate to be a boolean, but got ${typeof options.allowCreate}: ${String(options.allowCreate)}.`);
62
+ const rawOptions = options;
63
+ if ("mustExist" in options && rawOptions.mustExist !== void 0 && typeof rawOptions.mustExist !== "boolean") throw new TypeError(`Expected mustExist to be a boolean, but got ${typeof rawOptions.mustExist}: ${String(rawOptions.mustExist)}.`);
64
+ if ("mustNotExist" in options && rawOptions.mustNotExist !== void 0 && typeof rawOptions.mustNotExist !== "boolean") throw new TypeError(`Expected mustNotExist to be a boolean, but got ${typeof rawOptions.mustNotExist}: ${String(rawOptions.mustNotExist)}.`);
46
65
  const mustExist = "mustExist" in options ? options.mustExist : false;
47
66
  const mustNotExist = "mustNotExist" in options ? options.mustNotExist : false;
67
+ if (mustExist && mustNotExist) throw new TypeError("Options mustExist and mustNotExist are mutually exclusive.");
48
68
  return {
49
69
  $mode: "sync",
50
70
  metavar,
51
71
  parse(input) {
52
- if (extensions && extensions.length > 0) {
53
- const ext = (0, node_path.extname)(input);
54
- if (!extensions.includes(ext)) {
55
- const actualExt = ext || "no extension";
72
+ if (input.trim() === "") return {
73
+ success: false,
74
+ error: options.errors?.emptyPath ? typeof options.errors.emptyPath === "function" ? options.errors.emptyPath(input) : options.errors.emptyPath : __optique_core_message.message`Path must not be empty.`
75
+ };
76
+ if (type !== "directory" && extensions && extensions.length > 0) {
77
+ const base = /[/\\]$/.test(input) ? "" : (0, node_path.basename)(input);
78
+ if (!extensions.some((ext) => base.endsWith(ext))) {
79
+ const ext = (0, node_path.extname)(input);
80
+ const actualExt = ext || (base.startsWith(".") ? base : "no extension");
56
81
  return {
57
82
  success: false,
58
83
  error: options.errors?.invalidExtension ? typeof options.errors.invalidExtension === "function" ? options.errors.invalidExtension(input, extensions, actualExt) : options.errors.invalidExtension : __optique_core_message.message`Expected file with extension ${(0, __optique_core_message.text)(extensions.join(", "))}, got ${(0, __optique_core_message.text)(actualExt)}.`
@@ -112,7 +137,7 @@ function path(options = {}) {
112
137
  pattern: prefix,
113
138
  type: type === "either" ? "any" : type,
114
139
  extensions,
115
- includeHidden: prefix.startsWith("."),
140
+ includeHidden: (0, node_path.basename)(prefix).startsWith("."),
116
141
  description: type === "directory" ? __optique_core_message.message`Directory` : type === "file" ? __optique_core_message.message`File` : __optique_core_message.message`File or directory`
117
142
  };
118
143
  }