@optique/man 1.0.0-dev.921 → 1.0.0

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/cli.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- const require_man = require('./man-Bah5ZMyV.cjs');
3
- require('./roff-Cl0vekdg.cjs');
4
- const require_generator = require('./generator-bugVqGJn.cjs');
2
+ const require_man = require('./man-CkvscTlM.cjs');
3
+ require('./roff-DJ-LkRXl.cjs');
4
+ const require_generator = require('./generator-DXD_dkL9.cjs');
5
5
  const __optique_core_constructs = require_man.__toESM(require("@optique/core/constructs"));
6
6
  const __optique_core_primitives = require_man.__toESM(require("@optique/core/primitives"));
7
7
  const __optique_core_valueparser = require_man.__toESM(require("@optique/core/valueparser"));
@@ -17,7 +17,7 @@ const node_url = require_man.__toESM(require("node:url"));
17
17
 
18
18
  //#region deno.json
19
19
  var name = "@optique/man";
20
- var version = "1.0.0-dev.921+754748bd";
20
+ var version = "1.0.0";
21
21
  var license = "MIT";
22
22
  var exports$1 = {
23
23
  ".": "./src/index.ts",
@@ -341,7 +341,7 @@ function isProgram(value) {
341
341
  */
342
342
  function isParser(value) {
343
343
  try {
344
- return value != null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "$mode" in value && "usage" in value && "getDocFragments" in value && typeof value.getDocFragments === "function";
344
+ return value != null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "mode" in value && "usage" in value && "getDocFragments" in value && typeof value.getDocFragments === "function";
345
345
  } catch {
346
346
  return false;
347
347
  }
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import "./roff-CwBRpWCo.js";
3
- import "./man-BiY491LQ.js";
4
- import { generateManPageAsync } from "./generator-CnMxnsMc.js";
2
+ import "./roff-CCdIQO7B.js";
3
+ import "./man-BE1OY_lJ.js";
4
+ import { generateManPageAsync } from "./generator-D5cbsYWL.js";
5
5
  import { object } from "@optique/core/constructs";
6
6
  import { argument, option } from "@optique/core/primitives";
7
7
  import { choice, string } from "@optique/core/valueparser";
@@ -17,7 +17,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
17
17
 
18
18
  //#region deno.json
19
19
  var name = "@optique/man";
20
- var version = "1.0.0-dev.921+754748bd";
20
+ var version = "1.0.0";
21
21
  var license = "MIT";
22
22
  var exports = {
23
23
  ".": "./src/index.ts",
@@ -341,7 +341,7 @@ function isProgram(value) {
341
341
  */
342
342
  function isParser(value) {
343
343
  try {
344
- return value != null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "$mode" in value && "usage" in value && "getDocFragments" in value && typeof value.getDocFragments === "function";
344
+ return value != null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "mode" in value && "usage" in value && "getDocFragments" in value && typeof value.getDocFragments === "function";
345
345
  } catch {
346
346
  return false;
347
347
  }
@@ -1,4 +1,4 @@
1
- import { formatDocPageAsMan } from "./man-BiY491LQ.js";
1
+ import { formatDocPageAsMan } from "./man-BE1OY_lJ.js";
2
2
  import { getDocPageAsync, getDocPageSync } from "@optique/core/parser";
3
3
 
4
4
  //#region src/generator.ts
@@ -9,7 +9,7 @@ function isParser(value) {
9
9
  try {
10
10
  if (value == null || typeof value !== "object") return false;
11
11
  const p = value;
12
- return "parse" in p && typeof p.parse === "function" && "complete" in p && typeof p.complete === "function" && "$mode" in p && (p.$mode === "sync" || p.$mode === "async") && "usage" in p && Array.isArray(p.usage) && "initialState" in p && "suggest" in p && typeof p.suggest === "function" && "getDocFragments" in p && typeof p.getDocFragments === "function";
12
+ return "parse" in p && typeof p.parse === "function" && "complete" in p && typeof p.complete === "function" && "mode" in p && (p.mode === "sync" || p.mode === "async") && "usage" in p && Array.isArray(p.usage) && "initialState" in p && "suggest" in p && typeof p.suggest === "function" && "getDocFragments" in p && typeof p.getDocFragments === "function";
13
13
  } catch {
14
14
  return false;
15
15
  }
@@ -50,6 +50,9 @@ function extractParserAndOptions(parserOrProgram, options) {
50
50
  author: programOptions.author ?? metadata.author,
51
51
  bugs: programOptions.bugs ?? metadata.bugs,
52
52
  examples: programOptions.examples ?? metadata.examples,
53
+ brief: programOptions.brief ?? metadata.brief,
54
+ description: programOptions.description ?? metadata.description,
55
+ footer: programOptions.footer ?? metadata.footer,
53
56
  seeAlso: programOptions.seeAlso,
54
57
  environment: programOptions.environment,
55
58
  files: programOptions.files,
@@ -65,6 +68,7 @@ function extractParserAndOptions(parserOrProgram, options) {
65
68
  }
66
69
  function generateManPageSync(parserOrProgram, options) {
67
70
  const { parser, mergedOptions } = extractParserAndOptions(parserOrProgram, options);
71
+ if (parser.mode === "async") throw new TypeError("Cannot use an async parser with generateManPageSync(). Use generateManPageAsync() or generateManPage() instead.");
68
72
  const docPage = getDocPageSync(parser) ?? { sections: [] };
69
73
  return formatDocPageAsMan(docPage, mergedOptions);
70
74
  }
@@ -75,7 +79,7 @@ async function generateManPageAsync(parserOrProgram, options) {
75
79
  }
76
80
  function generateManPage(parserOrProgram, options) {
77
81
  const { parser, mergedOptions } = extractParserAndOptions(parserOrProgram, options);
78
- if (parser.$mode === "async") return generateManPageAsync(parser, mergedOptions);
82
+ if (parser.mode === "async") return generateManPageAsync(parser, mergedOptions);
79
83
  return generateManPageSync(parser, mergedOptions);
80
84
  }
81
85
 
@@ -1,4 +1,4 @@
1
- const require_man = require('./man-Bah5ZMyV.cjs');
1
+ const require_man = require('./man-CkvscTlM.cjs');
2
2
  const __optique_core_parser = require_man.__toESM(require("@optique/core/parser"));
3
3
 
4
4
  //#region src/generator.ts
@@ -9,7 +9,7 @@ function isParser(value) {
9
9
  try {
10
10
  if (value == null || typeof value !== "object") return false;
11
11
  const p = value;
12
- return "parse" in p && typeof p.parse === "function" && "complete" in p && typeof p.complete === "function" && "$mode" in p && (p.$mode === "sync" || p.$mode === "async") && "usage" in p && Array.isArray(p.usage) && "initialState" in p && "suggest" in p && typeof p.suggest === "function" && "getDocFragments" in p && typeof p.getDocFragments === "function";
12
+ return "parse" in p && typeof p.parse === "function" && "complete" in p && typeof p.complete === "function" && "mode" in p && (p.mode === "sync" || p.mode === "async") && "usage" in p && Array.isArray(p.usage) && "initialState" in p && "suggest" in p && typeof p.suggest === "function" && "getDocFragments" in p && typeof p.getDocFragments === "function";
13
13
  } catch {
14
14
  return false;
15
15
  }
@@ -50,6 +50,9 @@ function extractParserAndOptions(parserOrProgram, options) {
50
50
  author: programOptions.author ?? metadata.author,
51
51
  bugs: programOptions.bugs ?? metadata.bugs,
52
52
  examples: programOptions.examples ?? metadata.examples,
53
+ brief: programOptions.brief ?? metadata.brief,
54
+ description: programOptions.description ?? metadata.description,
55
+ footer: programOptions.footer ?? metadata.footer,
53
56
  seeAlso: programOptions.seeAlso,
54
57
  environment: programOptions.environment,
55
58
  files: programOptions.files,
@@ -65,6 +68,7 @@ function extractParserAndOptions(parserOrProgram, options) {
65
68
  }
66
69
  function generateManPageSync(parserOrProgram, options) {
67
70
  const { parser, mergedOptions } = extractParserAndOptions(parserOrProgram, options);
71
+ if (parser.mode === "async") throw new TypeError("Cannot use an async parser with generateManPageSync(). Use generateManPageAsync() or generateManPage() instead.");
68
72
  const docPage = (0, __optique_core_parser.getDocPageSync)(parser) ?? { sections: [] };
69
73
  return require_man.formatDocPageAsMan(docPage, mergedOptions);
70
74
  }
@@ -75,7 +79,7 @@ async function generateManPageAsync(parserOrProgram, options) {
75
79
  }
76
80
  function generateManPage(parserOrProgram, options) {
77
81
  const { parser, mergedOptions } = extractParserAndOptions(parserOrProgram, options);
78
- if (parser.$mode === "async") return generateManPageAsync(parser, mergedOptions);
82
+ if (parser.mode === "async") return generateManPageAsync(parser, mergedOptions);
79
83
  return generateManPageSync(parser, mergedOptions);
80
84
  }
81
85
 
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
- const require_man = require('./man-Bah5ZMyV.cjs');
2
- const require_roff = require('./roff-Cl0vekdg.cjs');
3
- const require_generator = require('./generator-bugVqGJn.cjs');
1
+ const require_man = require('./man-CkvscTlM.cjs');
2
+ const require_roff = require('./roff-DJ-LkRXl.cjs');
3
+ const require_generator = require('./generator-DXD_dkL9.cjs');
4
4
 
5
5
  exports.escapeHyphens = require_roff.escapeHyphens;
6
6
  exports.escapeQuotedValue = require_roff.escapeQuotedValue;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-C_87xXP6.cjs";
2
- import { ManPageOptions, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-Dp4oW-al.cjs";
1
+ import { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-COeDIWWF.cjs";
2
+ import { ManPageOptions, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-DKwa47XV.cjs";
3
3
  import { Mode, ModeValue, Parser } from "@optique/core/parser";
4
4
  import { Program } from "@optique/core/program";
5
5
 
@@ -14,10 +14,10 @@ interface GenerateManPageOptions extends ManPageOptions {}
14
14
  /**
15
15
  * Options for generating a man page from a {@link Program}.
16
16
  *
17
- * This interface omits `name`, `version`, `author`, `bugs`, and `examples`
18
- * from {@link ManPageOptions} since they are extracted from the program's
19
- * metadata. You can still override them by providing values in this options
20
- * object.
17
+ * For Program inputs, `name`, `version`, `author`, `bugs`, `examples`,
18
+ * `brief`, `description`, and `footer` default to values from
19
+ * `program.metadata`.
20
+ * These fields are optional here and may be provided to override metadata.
21
21
  *
22
22
  * @since 0.10.0
23
23
  */
@@ -159,4 +159,4 @@ declare function generateManPage(parser: Parser<"sync", unknown, unknown>, optio
159
159
  declare function generateManPage(parser: Parser<"async", unknown, unknown>, options: GenerateManPageOptions): Promise<string>;
160
160
  declare function generateManPage<M extends Mode>(parser: Parser<M, unknown, unknown>, options: GenerateManPageOptions): ModeValue<M, string>;
161
161
  //#endregion
162
- export { type GenerateManPageOptions, type GenerateManPageProgramOptions, type ManPageOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatDateForMan, formatDocPageAsMan, formatMessageAsRoff, formatUsageTermAsRoff, generateManPage, generateManPageAsync, generateManPageSync };
162
+ export { type GenerateManPageOptions, type GenerateManPageProgramOptions, type ManPageOptions, type RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatDateForMan, formatDocPageAsMan, formatMessageAsRoff, formatUsageTermAsRoff, generateManPage, generateManPageAsync, generateManPageSync };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-DlmeL3KB.js";
2
- import { ManPageOptions, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-CGBnZvZF.js";
1
+ import { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-CgYQDHBN.js";
2
+ import { ManPageOptions, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-B1Q2mhit.js";
3
3
  import { Program } from "@optique/core/program";
4
4
  import { Mode, ModeValue, Parser } from "@optique/core/parser";
5
5
 
@@ -14,10 +14,10 @@ interface GenerateManPageOptions extends ManPageOptions {}
14
14
  /**
15
15
  * Options for generating a man page from a {@link Program}.
16
16
  *
17
- * This interface omits `name`, `version`, `author`, `bugs`, and `examples`
18
- * from {@link ManPageOptions} since they are extracted from the program's
19
- * metadata. You can still override them by providing values in this options
20
- * object.
17
+ * For Program inputs, `name`, `version`, `author`, `bugs`, `examples`,
18
+ * `brief`, `description`, and `footer` default to values from
19
+ * `program.metadata`.
20
+ * These fields are optional here and may be provided to override metadata.
21
21
  *
22
22
  * @since 0.10.0
23
23
  */
@@ -159,4 +159,4 @@ declare function generateManPage(parser: Parser<"sync", unknown, unknown>, optio
159
159
  declare function generateManPage(parser: Parser<"async", unknown, unknown>, options: GenerateManPageOptions): Promise<string>;
160
160
  declare function generateManPage<M extends Mode>(parser: Parser<M, unknown, unknown>, options: GenerateManPageOptions): ModeValue<M, string>;
161
161
  //#endregion
162
- export { type GenerateManPageOptions, type GenerateManPageProgramOptions, type ManPageOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatDateForMan, formatDocPageAsMan, formatMessageAsRoff, formatUsageTermAsRoff, generateManPage, generateManPageAsync, generateManPageSync };
162
+ export { type GenerateManPageOptions, type GenerateManPageProgramOptions, type ManPageOptions, type RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatDateForMan, formatDocPageAsMan, formatMessageAsRoff, formatUsageTermAsRoff, generateManPage, generateManPageAsync, generateManPageSync };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-CwBRpWCo.js";
2
- import { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-BiY491LQ.js";
3
- import { generateManPage, generateManPageAsync, generateManPageSync } from "./generator-CnMxnsMc.js";
1
+ import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-CCdIQO7B.js";
2
+ import { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-BE1OY_lJ.js";
3
+ import { generateManPage, generateManPageAsync, generateManPageSync } from "./generator-D5cbsYWL.js";
4
4
 
5
5
  export { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatDateForMan, formatDocPageAsMan, formatMessageAsRoff, formatUsageTermAsRoff, generateManPage, generateManPageAsync, generateManPageSync };
@@ -78,6 +78,24 @@ interface ManPageOptions {
78
78
  * Exit status codes to document in the EXIT STATUS section.
79
79
  */
80
80
  readonly exitStatus?: DocSection;
81
+ /**
82
+ * A brief description of the program for the NAME section.
83
+ * Overrides the brief from the {@link DocPage} if both are present.
84
+ * @since 1.0.0
85
+ */
86
+ readonly brief?: Message;
87
+ /**
88
+ * A detailed description of the program for the DESCRIPTION section.
89
+ * Overrides the description from the {@link DocPage} if both are present.
90
+ * @since 1.0.0
91
+ */
92
+ readonly description?: Message;
93
+ /**
94
+ * Footer text appended at the end of the man page.
95
+ * Overrides the footer from the {@link DocPage} if both are present.
96
+ * @since 1.0.0
97
+ */
98
+ readonly footer?: Message;
81
99
  }
82
100
  /**
83
101
  * Formats a date for use in man pages.
@@ -90,6 +108,7 @@ interface ManPageOptions {
90
108
  *
91
109
  * @param date The date to format, or undefined.
92
110
  * @returns The formatted date string, or undefined.
111
+ * @throws {RangeError} If the given `Date` object is invalid.
93
112
  * @since 0.10.0
94
113
  */
95
114
  declare function formatDateForMan(date: string | Date | undefined): string | undefined;
@@ -1,4 +1,4 @@
1
- import { escapeHyphens, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-CwBRpWCo.js";
1
+ import { escapeHyphens, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-CCdIQO7B.js";
2
2
  import { isDocHidden, isUsageHidden } from "@optique/core/usage";
3
3
 
4
4
  //#region src/man.ts
@@ -13,11 +13,13 @@ import { isDocHidden, isUsageHidden } from "@optique/core/usage";
13
13
  *
14
14
  * @param date The date to format, or undefined.
15
15
  * @returns The formatted date string, or undefined.
16
+ * @throws {RangeError} If the given `Date` object is invalid.
16
17
  * @since 0.10.0
17
18
  */
18
19
  function formatDateForMan(date) {
19
20
  if (date === void 0) return void 0;
20
21
  if (typeof date === "string") return date;
22
+ if (Number.isNaN(date.getTime())) throw new RangeError("Invalid Date object.");
21
23
  const months = [
22
24
  "January",
23
25
  "February",
@@ -46,28 +48,61 @@ function formatCommandNameAsRoff(name) {
46
48
  * @since 0.10.0
47
49
  */
48
50
  function formatUsageTermAsRoff(term) {
51
+ return formatUsageTermAsRoffInternal(term, false);
52
+ }
53
+ /**
54
+ * Returns whether a usage list contains exactly one visible term that
55
+ * produces its own brackets. When true, a parent wrapper can safely elide
56
+ * its own brackets to avoid redundant nesting. Multiple bracket-producing
57
+ * siblings must keep their individual brackets for disambiguation.
58
+ */
59
+ function hasSingleBracketedTerm(terms) {
60
+ const visible = terms.filter((t$1) => !("hidden" in t$1 && isUsageHidden(t$1.hidden)));
61
+ if (visible.length !== 1) return false;
62
+ const t = visible[0];
63
+ return t.type === "option" || t.type === "optional" || t.type === "multiple" && t.min < 1 || t.type === "passthrough";
64
+ }
65
+ /**
66
+ * Returns whether a usage list's single visible term has the given type.
67
+ */
68
+ function hasSingleVisibleTermOfType(terms, type) {
69
+ const visible = terms.filter((t) => !("hidden" in t && isUsageHidden(t.hidden)));
70
+ return visible.length === 1 && visible[0].type === type;
71
+ }
72
+ function formatUsageTermAsRoffInternal(term, insideBrackets) {
49
73
  if ("hidden" in term && isUsageHidden(term.hidden)) return "";
50
74
  switch (term.type) {
51
75
  case "argument": return `\\fI${escapeRoff(term.metavar)}\\fR`;
52
76
  case "option": {
53
77
  const names = term.names.map((name) => `\\fB${escapeHyphens(name)}\\fR`).join(" | ");
54
78
  const metavarPart = term.metavar ? ` \\fI${escapeRoff(term.metavar)}\\fR` : "";
79
+ if (insideBrackets) return `${names}${metavarPart}`;
55
80
  return `[${names}${metavarPart}]`;
56
81
  }
57
82
  case "command": return formatCommandNameAsRoff(term.name);
58
83
  case "optional": {
59
- const inner = formatUsageAsRoff(term.terms);
84
+ const childrenBracketed = hasSingleBracketedTerm(term.terms);
85
+ const childIsMultiple = childrenBracketed && hasSingleVisibleTermOfType(term.terms, "multiple");
86
+ const inner = formatUsageAsRoffInternal(term.terms, childrenBracketed);
60
87
  if (inner === "") return "";
88
+ if (insideBrackets && childrenBracketed && !childIsMultiple) return inner;
61
89
  return `[${inner}]`;
62
90
  }
63
91
  case "multiple": {
64
- const inner = formatUsageAsRoff(term.terms);
92
+ const wrapInBrackets = term.min < 1;
93
+ const childrenBracketed = hasSingleBracketedTerm(term.terms);
94
+ const childIsMultiple = childrenBracketed && hasSingleVisibleTermOfType(term.terms, "multiple");
95
+ const passInsideBrackets = wrapInBrackets && childrenBracketed && !childIsMultiple;
96
+ const inner = formatUsageAsRoffInternal(term.terms, passInsideBrackets);
65
97
  if (inner === "") return "";
66
- if (term.min < 1) return `[${inner} ...]`;
98
+ if (wrapInBrackets) {
99
+ if (insideBrackets && passInsideBrackets) return `${inner} ...`;
100
+ return `[${inner} ...]`;
101
+ }
67
102
  return `${inner} ...`;
68
103
  }
69
104
  case "exclusive": {
70
- const alternatives = term.terms.map((t) => formatUsageAsRoff(t)).filter((s) => s !== "");
105
+ const alternatives = term.terms.map((t) => formatUsageAsRoffInternal(t, false)).filter((s) => s !== "");
71
106
  if (alternatives.length === 0) return "";
72
107
  if (alternatives.length === 1) return alternatives[0];
73
108
  return `(${alternatives.join(" | ")})`;
@@ -88,7 +123,10 @@ function formatUsageTermAsRoff(term) {
88
123
  * @returns The roff-formatted string.
89
124
  */
90
125
  function formatUsageAsRoff(usage) {
91
- return usage.map(formatUsageTermAsRoff).filter((s) => s !== "").join(" ");
126
+ return formatUsageAsRoffInternal(usage, false);
127
+ }
128
+ function formatUsageAsRoffInternal(usage, insideBrackets) {
129
+ return usage.map((term) => formatUsageTermAsRoffInternal(term, insideBrackets)).filter((s) => s !== "").join(" ");
92
130
  }
93
131
  /**
94
132
  * Formats a {@link DocEntry}'s term for man page output.
@@ -160,6 +198,26 @@ function formatDocUsageAsRoff(usage) {
160
198
  return usage.map(formatDocUsageTermAsRoff).filter((s) => s !== "").join(" ");
161
199
  }
162
200
  /**
201
+ * Infers the section title from entry kinds when no explicit title is given.
202
+ * Returns `"COMMANDS"` for command-only sections, `"ARGUMENTS"` for
203
+ * argument-only sections, and `"OPTIONS"` otherwise.
204
+ *
205
+ * @param entries The entries in the section.
206
+ * @returns The inferred section title in uppercase.
207
+ */
208
+ function inferSectionTitle(entries) {
209
+ const kinds = /* @__PURE__ */ new Set();
210
+ for (const entry of entries) {
211
+ if ("hidden" in entry.term && isDocHidden(entry.term.hidden)) continue;
212
+ kinds.add(entry.term.type);
213
+ }
214
+ if (kinds.size === 1) {
215
+ if (kinds.has("command")) return "COMMANDS";
216
+ if (kinds.has("argument")) return "ARGUMENTS";
217
+ }
218
+ return "OPTIONS";
219
+ }
220
+ /**
163
221
  * Formats a {@link DocSection} as roff markup with .TP macros.
164
222
  *
165
223
  * @param section The section to format.
@@ -175,8 +233,14 @@ function formatDocSectionEntries(section) {
175
233
  if (entry.description) {
176
234
  let desc = formatMessageAsRoff(entry.description);
177
235
  if (entry.default) desc += ` [${formatMessageAsRoff(entry.default)}]`;
236
+ if (entry.choices) desc += ` (choices: ${formatMessageAsRoff(entry.choices, { quotes: false })})`;
178
237
  lines.push(desc);
179
- } else if (entry.default) lines.push(`[${formatMessageAsRoff(entry.default)}]`);
238
+ } else if (entry.default || entry.choices) {
239
+ const parts = [];
240
+ if (entry.default) parts.push(`[${formatMessageAsRoff(entry.default)}]`);
241
+ if (entry.choices) parts.push(`(choices: ${formatMessageAsRoff(entry.choices, { quotes: false })})`);
242
+ lines.push(parts.join(" "));
243
+ }
180
244
  }
181
245
  return lines.join("\n");
182
246
  }
@@ -235,7 +299,8 @@ function formatDocPageAsMan(page, options) {
235
299
  if (hasManual) thParts.push(`"${escapeRequestArg(options.manual)}"`);
236
300
  lines.push(`.TH ${thParts.join(" ")}`);
237
301
  lines.push(".SH NAME");
238
- if (page.brief) lines.push(`${escapeHyphens(escapeRoff(options.name))} \\- ${formatMessageAsRoff(page.brief)}`);
302
+ const brief = options.brief ?? page.brief;
303
+ if (brief) lines.push(`${escapeHyphens(escapeRoff(options.name))} \\- ${formatMessageAsRoff(brief)}`);
239
304
  else lines.push(escapeHyphens(escapeRoff(options.name)));
240
305
  if (page.usage) {
241
306
  lines.push(".SH SYNOPSIS");
@@ -243,15 +308,16 @@ function formatDocPageAsMan(page, options) {
243
308
  const usageStr = formatUsageAsRoff(page.usage);
244
309
  if (usageStr) lines.push(usageStr);
245
310
  }
246
- if (page.description) {
311
+ const description = options.description ?? page.description;
312
+ if (description) {
247
313
  lines.push(".SH DESCRIPTION");
248
- lines.push(formatMessageAsRoff(page.description));
314
+ lines.push(formatMessageAsRoff(description));
249
315
  }
250
316
  for (const section of page.sections) {
251
317
  if (section.entries.length === 0) continue;
252
318
  const content = formatDocSectionEntries(section);
253
319
  if (content === "") continue;
254
- const title = section.title?.toUpperCase() ?? "OPTIONS";
320
+ const title = section.title?.toUpperCase() ?? inferSectionTitle(section.entries);
255
321
  lines.push(`.SH "${escapeRequestArg(title)}"`);
256
322
  lines.push(content);
257
323
  }
@@ -276,13 +342,15 @@ function formatDocPageAsMan(page, options) {
276
342
  lines.push(content);
277
343
  }
278
344
  }
279
- if (options.examples) {
345
+ const examples = options.examples ?? page.examples;
346
+ if (examples) {
280
347
  lines.push(".SH EXAMPLES");
281
- lines.push(formatMessageAsRoff(options.examples));
348
+ lines.push(formatMessageAsRoff(examples));
282
349
  }
283
- if (options.bugs) {
350
+ const bugs = options.bugs ?? page.bugs;
351
+ if (bugs) {
284
352
  lines.push(".SH BUGS");
285
- lines.push(formatMessageAsRoff(options.bugs));
353
+ lines.push(formatMessageAsRoff(bugs));
286
354
  }
287
355
  if (options.seeAlso && options.seeAlso.length > 0) {
288
356
  for (const ref of options.seeAlso) if (!Number.isInteger(ref.section) || ref.section < 1 || ref.section > 8) {
@@ -301,13 +369,15 @@ function formatDocPageAsMan(page, options) {
301
369
  });
302
370
  lines.push(refs.join("\n"));
303
371
  }
304
- if (options.author) {
372
+ const author = options.author ?? page.author;
373
+ if (author) {
305
374
  lines.push(".SH AUTHOR");
306
- lines.push(formatMessageAsRoff(options.author));
375
+ lines.push(formatMessageAsRoff(author));
307
376
  }
308
- if (page.footer) {
377
+ const footer = options.footer ?? page.footer;
378
+ if (footer) {
309
379
  lines.push(".PP");
310
- lines.push(formatMessageAsRoff(page.footer));
380
+ lines.push(formatMessageAsRoff(footer));
311
381
  }
312
382
  return lines.join("\n");
313
383
  }
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
- const require_roff = require('./roff-Cl0vekdg.cjs');
24
+ const require_roff = require('./roff-DJ-LkRXl.cjs');
25
25
  const __optique_core_usage = __toESM(require("@optique/core/usage"));
26
26
 
27
27
  //#region src/man.ts
@@ -36,11 +36,13 @@ const __optique_core_usage = __toESM(require("@optique/core/usage"));
36
36
  *
37
37
  * @param date The date to format, or undefined.
38
38
  * @returns The formatted date string, or undefined.
39
+ * @throws {RangeError} If the given `Date` object is invalid.
39
40
  * @since 0.10.0
40
41
  */
41
42
  function formatDateForMan(date) {
42
43
  if (date === void 0) return void 0;
43
44
  if (typeof date === "string") return date;
45
+ if (Number.isNaN(date.getTime())) throw new RangeError("Invalid Date object.");
44
46
  const months = [
45
47
  "January",
46
48
  "February",
@@ -69,28 +71,61 @@ function formatCommandNameAsRoff(name) {
69
71
  * @since 0.10.0
70
72
  */
71
73
  function formatUsageTermAsRoff(term) {
74
+ return formatUsageTermAsRoffInternal(term, false);
75
+ }
76
+ /**
77
+ * Returns whether a usage list contains exactly one visible term that
78
+ * produces its own brackets. When true, a parent wrapper can safely elide
79
+ * its own brackets to avoid redundant nesting. Multiple bracket-producing
80
+ * siblings must keep their individual brackets for disambiguation.
81
+ */
82
+ function hasSingleBracketedTerm(terms) {
83
+ const visible = terms.filter((t$1) => !("hidden" in t$1 && (0, __optique_core_usage.isUsageHidden)(t$1.hidden)));
84
+ if (visible.length !== 1) return false;
85
+ const t = visible[0];
86
+ return t.type === "option" || t.type === "optional" || t.type === "multiple" && t.min < 1 || t.type === "passthrough";
87
+ }
88
+ /**
89
+ * Returns whether a usage list's single visible term has the given type.
90
+ */
91
+ function hasSingleVisibleTermOfType(terms, type) {
92
+ const visible = terms.filter((t) => !("hidden" in t && (0, __optique_core_usage.isUsageHidden)(t.hidden)));
93
+ return visible.length === 1 && visible[0].type === type;
94
+ }
95
+ function formatUsageTermAsRoffInternal(term, insideBrackets) {
72
96
  if ("hidden" in term && (0, __optique_core_usage.isUsageHidden)(term.hidden)) return "";
73
97
  switch (term.type) {
74
98
  case "argument": return `\\fI${require_roff.escapeRoff(term.metavar)}\\fR`;
75
99
  case "option": {
76
100
  const names = term.names.map((name) => `\\fB${require_roff.escapeHyphens(name)}\\fR`).join(" | ");
77
101
  const metavarPart = term.metavar ? ` \\fI${require_roff.escapeRoff(term.metavar)}\\fR` : "";
102
+ if (insideBrackets) return `${names}${metavarPart}`;
78
103
  return `[${names}${metavarPart}]`;
79
104
  }
80
105
  case "command": return formatCommandNameAsRoff(term.name);
81
106
  case "optional": {
82
- const inner = formatUsageAsRoff(term.terms);
107
+ const childrenBracketed = hasSingleBracketedTerm(term.terms);
108
+ const childIsMultiple = childrenBracketed && hasSingleVisibleTermOfType(term.terms, "multiple");
109
+ const inner = formatUsageAsRoffInternal(term.terms, childrenBracketed);
83
110
  if (inner === "") return "";
111
+ if (insideBrackets && childrenBracketed && !childIsMultiple) return inner;
84
112
  return `[${inner}]`;
85
113
  }
86
114
  case "multiple": {
87
- const inner = formatUsageAsRoff(term.terms);
115
+ const wrapInBrackets = term.min < 1;
116
+ const childrenBracketed = hasSingleBracketedTerm(term.terms);
117
+ const childIsMultiple = childrenBracketed && hasSingleVisibleTermOfType(term.terms, "multiple");
118
+ const passInsideBrackets = wrapInBrackets && childrenBracketed && !childIsMultiple;
119
+ const inner = formatUsageAsRoffInternal(term.terms, passInsideBrackets);
88
120
  if (inner === "") return "";
89
- if (term.min < 1) return `[${inner} ...]`;
121
+ if (wrapInBrackets) {
122
+ if (insideBrackets && passInsideBrackets) return `${inner} ...`;
123
+ return `[${inner} ...]`;
124
+ }
90
125
  return `${inner} ...`;
91
126
  }
92
127
  case "exclusive": {
93
- const alternatives = term.terms.map((t) => formatUsageAsRoff(t)).filter((s) => s !== "");
128
+ const alternatives = term.terms.map((t) => formatUsageAsRoffInternal(t, false)).filter((s) => s !== "");
94
129
  if (alternatives.length === 0) return "";
95
130
  if (alternatives.length === 1) return alternatives[0];
96
131
  return `(${alternatives.join(" | ")})`;
@@ -111,7 +146,10 @@ function formatUsageTermAsRoff(term) {
111
146
  * @returns The roff-formatted string.
112
147
  */
113
148
  function formatUsageAsRoff(usage) {
114
- return usage.map(formatUsageTermAsRoff).filter((s) => s !== "").join(" ");
149
+ return formatUsageAsRoffInternal(usage, false);
150
+ }
151
+ function formatUsageAsRoffInternal(usage, insideBrackets) {
152
+ return usage.map((term) => formatUsageTermAsRoffInternal(term, insideBrackets)).filter((s) => s !== "").join(" ");
115
153
  }
116
154
  /**
117
155
  * Formats a {@link DocEntry}'s term for man page output.
@@ -183,6 +221,26 @@ function formatDocUsageAsRoff(usage) {
183
221
  return usage.map(formatDocUsageTermAsRoff).filter((s) => s !== "").join(" ");
184
222
  }
185
223
  /**
224
+ * Infers the section title from entry kinds when no explicit title is given.
225
+ * Returns `"COMMANDS"` for command-only sections, `"ARGUMENTS"` for
226
+ * argument-only sections, and `"OPTIONS"` otherwise.
227
+ *
228
+ * @param entries The entries in the section.
229
+ * @returns The inferred section title in uppercase.
230
+ */
231
+ function inferSectionTitle(entries) {
232
+ const kinds = /* @__PURE__ */ new Set();
233
+ for (const entry of entries) {
234
+ if ("hidden" in entry.term && (0, __optique_core_usage.isDocHidden)(entry.term.hidden)) continue;
235
+ kinds.add(entry.term.type);
236
+ }
237
+ if (kinds.size === 1) {
238
+ if (kinds.has("command")) return "COMMANDS";
239
+ if (kinds.has("argument")) return "ARGUMENTS";
240
+ }
241
+ return "OPTIONS";
242
+ }
243
+ /**
186
244
  * Formats a {@link DocSection} as roff markup with .TP macros.
187
245
  *
188
246
  * @param section The section to format.
@@ -198,8 +256,14 @@ function formatDocSectionEntries(section) {
198
256
  if (entry.description) {
199
257
  let desc = require_roff.formatMessageAsRoff(entry.description);
200
258
  if (entry.default) desc += ` [${require_roff.formatMessageAsRoff(entry.default)}]`;
259
+ if (entry.choices) desc += ` (choices: ${require_roff.formatMessageAsRoff(entry.choices, { quotes: false })})`;
201
260
  lines.push(desc);
202
- } else if (entry.default) lines.push(`[${require_roff.formatMessageAsRoff(entry.default)}]`);
261
+ } else if (entry.default || entry.choices) {
262
+ const parts = [];
263
+ if (entry.default) parts.push(`[${require_roff.formatMessageAsRoff(entry.default)}]`);
264
+ if (entry.choices) parts.push(`(choices: ${require_roff.formatMessageAsRoff(entry.choices, { quotes: false })})`);
265
+ lines.push(parts.join(" "));
266
+ }
203
267
  }
204
268
  return lines.join("\n");
205
269
  }
@@ -258,7 +322,8 @@ function formatDocPageAsMan(page, options) {
258
322
  if (hasManual) thParts.push(`"${require_roff.escapeRequestArg(options.manual)}"`);
259
323
  lines.push(`.TH ${thParts.join(" ")}`);
260
324
  lines.push(".SH NAME");
261
- if (page.brief) lines.push(`${require_roff.escapeHyphens(require_roff.escapeRoff(options.name))} \\- ${require_roff.formatMessageAsRoff(page.brief)}`);
325
+ const brief = options.brief ?? page.brief;
326
+ if (brief) lines.push(`${require_roff.escapeHyphens(require_roff.escapeRoff(options.name))} \\- ${require_roff.formatMessageAsRoff(brief)}`);
262
327
  else lines.push(require_roff.escapeHyphens(require_roff.escapeRoff(options.name)));
263
328
  if (page.usage) {
264
329
  lines.push(".SH SYNOPSIS");
@@ -266,15 +331,16 @@ function formatDocPageAsMan(page, options) {
266
331
  const usageStr = formatUsageAsRoff(page.usage);
267
332
  if (usageStr) lines.push(usageStr);
268
333
  }
269
- if (page.description) {
334
+ const description = options.description ?? page.description;
335
+ if (description) {
270
336
  lines.push(".SH DESCRIPTION");
271
- lines.push(require_roff.formatMessageAsRoff(page.description));
337
+ lines.push(require_roff.formatMessageAsRoff(description));
272
338
  }
273
339
  for (const section of page.sections) {
274
340
  if (section.entries.length === 0) continue;
275
341
  const content = formatDocSectionEntries(section);
276
342
  if (content === "") continue;
277
- const title = section.title?.toUpperCase() ?? "OPTIONS";
343
+ const title = section.title?.toUpperCase() ?? inferSectionTitle(section.entries);
278
344
  lines.push(`.SH "${require_roff.escapeRequestArg(title)}"`);
279
345
  lines.push(content);
280
346
  }
@@ -299,13 +365,15 @@ function formatDocPageAsMan(page, options) {
299
365
  lines.push(content);
300
366
  }
301
367
  }
302
- if (options.examples) {
368
+ const examples = options.examples ?? page.examples;
369
+ if (examples) {
303
370
  lines.push(".SH EXAMPLES");
304
- lines.push(require_roff.formatMessageAsRoff(options.examples));
371
+ lines.push(require_roff.formatMessageAsRoff(examples));
305
372
  }
306
- if (options.bugs) {
373
+ const bugs = options.bugs ?? page.bugs;
374
+ if (bugs) {
307
375
  lines.push(".SH BUGS");
308
- lines.push(require_roff.formatMessageAsRoff(options.bugs));
376
+ lines.push(require_roff.formatMessageAsRoff(bugs));
309
377
  }
310
378
  if (options.seeAlso && options.seeAlso.length > 0) {
311
379
  for (const ref of options.seeAlso) if (!Number.isInteger(ref.section) || ref.section < 1 || ref.section > 8) {
@@ -324,13 +392,15 @@ function formatDocPageAsMan(page, options) {
324
392
  });
325
393
  lines.push(refs.join("\n"));
326
394
  }
327
- if (options.author) {
395
+ const author = options.author ?? page.author;
396
+ if (author) {
328
397
  lines.push(".SH AUTHOR");
329
- lines.push(require_roff.formatMessageAsRoff(options.author));
398
+ lines.push(require_roff.formatMessageAsRoff(author));
330
399
  }
331
- if (page.footer) {
400
+ const footer = options.footer ?? page.footer;
401
+ if (footer) {
332
402
  lines.push(".PP");
333
- lines.push(require_roff.formatMessageAsRoff(page.footer));
403
+ lines.push(require_roff.formatMessageAsRoff(footer));
334
404
  }
335
405
  return lines.join("\n");
336
406
  }
@@ -78,6 +78,24 @@ interface ManPageOptions {
78
78
  * Exit status codes to document in the EXIT STATUS section.
79
79
  */
80
80
  readonly exitStatus?: DocSection;
81
+ /**
82
+ * A brief description of the program for the NAME section.
83
+ * Overrides the brief from the {@link DocPage} if both are present.
84
+ * @since 1.0.0
85
+ */
86
+ readonly brief?: Message;
87
+ /**
88
+ * A detailed description of the program for the DESCRIPTION section.
89
+ * Overrides the description from the {@link DocPage} if both are present.
90
+ * @since 1.0.0
91
+ */
92
+ readonly description?: Message;
93
+ /**
94
+ * Footer text appended at the end of the man page.
95
+ * Overrides the footer from the {@link DocPage} if both are present.
96
+ * @since 1.0.0
97
+ */
98
+ readonly footer?: Message;
81
99
  }
82
100
  /**
83
101
  * Formats a date for use in man pages.
@@ -90,6 +108,7 @@ interface ManPageOptions {
90
108
  *
91
109
  * @param date The date to format, or undefined.
92
110
  * @returns The formatted date string, or undefined.
111
+ * @throws {RangeError} If the given `Date` object is invalid.
93
112
  * @since 0.10.0
94
113
  */
95
114
  declare function formatDateForMan(date: string | Date | undefined): string | undefined;
package/dist/man.cjs CHANGED
@@ -1,5 +1,5 @@
1
- const require_man = require('./man-Bah5ZMyV.cjs');
2
- require('./roff-Cl0vekdg.cjs');
1
+ const require_man = require('./man-CkvscTlM.cjs');
2
+ require('./roff-DJ-LkRXl.cjs');
3
3
 
4
4
  exports.formatDateForMan = require_man.formatDateForMan;
5
5
  exports.formatDocPageAsMan = require_man.formatDocPageAsMan;
package/dist/man.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-Dp4oW-al.cjs";
1
+ import { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-DKwa47XV.cjs";
2
2
  export { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff };
package/dist/man.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-CGBnZvZF.js";
1
+ import { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-B1Q2mhit.js";
2
2
  export { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff };
package/dist/man.js CHANGED
@@ -1,4 +1,4 @@
1
- import "./roff-CwBRpWCo.js";
2
- import { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-BiY491LQ.js";
1
+ import "./roff-CCdIQO7B.js";
2
+ import { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-BE1OY_lJ.js";
3
3
 
4
4
  export { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff };
@@ -96,17 +96,21 @@ function escapeHyphens(text) {
96
96
  * is done in formatMessageAsRoff after all terms are joined.
97
97
  *
98
98
  * @param term The message term to format.
99
+ * @param options Optional formatting options.
99
100
  * @returns The roff-formatted string.
100
101
  */
101
- function formatTermAsRoff(term) {
102
+ function formatTermAsRoff(term, options) {
102
103
  switch (term.type) {
103
104
  case "text": return escapeBackslashes(term.text);
104
105
  case "optionName": return `\\fB${escapeHyphens(term.optionName)}\\fR`;
105
106
  case "optionNames": return term.optionNames.map((name) => `\\fB${escapeHyphens(name)}\\fR`).join(", ");
106
107
  case "metavar": return `\\fI${escapeBackslashes(term.metavar)}\\fR`;
107
- case "value": return `"${escapeQuotedValue(term.value)}"`;
108
+ case "value":
109
+ if (options?.quotes === false) return escapeBackslashes(term.value);
110
+ return `"${escapeQuotedValue(term.value)}"`;
108
111
  case "values":
109
112
  if (term.values.length === 0) return "";
113
+ if (options?.quotes === false) return term.values.map((v) => escapeBackslashes(v)).join(" ");
110
114
  return term.values.map((v) => `"${escapeQuotedValue(v)}"`).join(" ");
111
115
  case "envVar": return `\\fB${escapeBackslashes(term.envVar)}\\fR`;
112
116
  case "commandLine": return `\\fB${escapeHyphens(escapeBackslashes(term.commandLine))}\\fR`;
@@ -148,11 +152,12 @@ function formatTermAsRoff(term) {
148
152
  * ```
149
153
  *
150
154
  * @param msg The message to format.
155
+ * @param options Optional formatting options.
151
156
  * @returns The roff-formatted string.
152
157
  * @since 0.10.0
153
158
  */
154
- function formatMessageAsRoff(msg) {
155
- const joined = msg.map(formatTermAsRoff).join("");
159
+ function formatMessageAsRoff(msg, options) {
160
+ const joined = msg.map((t) => formatTermAsRoff(t, options)).join("");
156
161
  const escaped = escapeLineStarts(joined);
157
162
  return escaped.replace(/\n\n+/g, "\n.PP\n");
158
163
  }
@@ -2,6 +2,20 @@ import { Message } from "@optique/core/message";
2
2
 
3
3
  //#region src/roff.d.ts
4
4
 
5
+ /**
6
+ * Options for roff formatting of messages.
7
+ *
8
+ * @since 1.0.0
9
+ */
10
+ interface RoffFormatOptions {
11
+ /**
12
+ * Whether to wrap `value` and `values` terms in double quotes.
13
+ * Defaults to `true`.
14
+ *
15
+ * @since 1.0.0
16
+ */
17
+ readonly quotes?: boolean;
18
+ }
5
19
  /**
6
20
  * Escapes special roff characters in plain text.
7
21
  *
@@ -89,9 +103,10 @@ declare function escapeHyphens(text: string): string;
89
103
  * ```
90
104
  *
91
105
  * @param msg The message to format.
106
+ * @param options Optional formatting options.
92
107
  * @returns The roff-formatted string.
93
108
  * @since 0.10.0
94
109
  */
95
- declare function formatMessageAsRoff(msg: Message): string;
110
+ declare function formatMessageAsRoff(msg: Message, options?: RoffFormatOptions): string;
96
111
  //#endregion
97
- export { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
112
+ export { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
@@ -2,6 +2,20 @@ import { Message } from "@optique/core/message";
2
2
 
3
3
  //#region src/roff.d.ts
4
4
 
5
+ /**
6
+ * Options for roff formatting of messages.
7
+ *
8
+ * @since 1.0.0
9
+ */
10
+ interface RoffFormatOptions {
11
+ /**
12
+ * Whether to wrap `value` and `values` terms in double quotes.
13
+ * Defaults to `true`.
14
+ *
15
+ * @since 1.0.0
16
+ */
17
+ readonly quotes?: boolean;
18
+ }
5
19
  /**
6
20
  * Escapes special roff characters in plain text.
7
21
  *
@@ -89,9 +103,10 @@ declare function escapeHyphens(text: string): string;
89
103
  * ```
90
104
  *
91
105
  * @param msg The message to format.
106
+ * @param options Optional formatting options.
92
107
  * @returns The roff-formatted string.
93
108
  * @since 0.10.0
94
109
  */
95
- declare function formatMessageAsRoff(msg: Message): string;
110
+ declare function formatMessageAsRoff(msg: Message, options?: RoffFormatOptions): string;
96
111
  //#endregion
97
- export { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
112
+ export { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
@@ -97,17 +97,21 @@ function escapeHyphens(text) {
97
97
  * is done in formatMessageAsRoff after all terms are joined.
98
98
  *
99
99
  * @param term The message term to format.
100
+ * @param options Optional formatting options.
100
101
  * @returns The roff-formatted string.
101
102
  */
102
- function formatTermAsRoff(term) {
103
+ function formatTermAsRoff(term, options) {
103
104
  switch (term.type) {
104
105
  case "text": return escapeBackslashes(term.text);
105
106
  case "optionName": return `\\fB${escapeHyphens(term.optionName)}\\fR`;
106
107
  case "optionNames": return term.optionNames.map((name) => `\\fB${escapeHyphens(name)}\\fR`).join(", ");
107
108
  case "metavar": return `\\fI${escapeBackslashes(term.metavar)}\\fR`;
108
- case "value": return `"${escapeQuotedValue(term.value)}"`;
109
+ case "value":
110
+ if (options?.quotes === false) return escapeBackslashes(term.value);
111
+ return `"${escapeQuotedValue(term.value)}"`;
109
112
  case "values":
110
113
  if (term.values.length === 0) return "";
114
+ if (options?.quotes === false) return term.values.map((v) => escapeBackslashes(v)).join(" ");
111
115
  return term.values.map((v) => `"${escapeQuotedValue(v)}"`).join(" ");
112
116
  case "envVar": return `\\fB${escapeBackslashes(term.envVar)}\\fR`;
113
117
  case "commandLine": return `\\fB${escapeHyphens(escapeBackslashes(term.commandLine))}\\fR`;
@@ -149,11 +153,12 @@ function formatTermAsRoff(term) {
149
153
  * ```
150
154
  *
151
155
  * @param msg The message to format.
156
+ * @param options Optional formatting options.
152
157
  * @returns The roff-formatted string.
153
158
  * @since 0.10.0
154
159
  */
155
- function formatMessageAsRoff(msg) {
156
- const joined = msg.map(formatTermAsRoff).join("");
160
+ function formatMessageAsRoff(msg, options) {
161
+ const joined = msg.map((t) => formatTermAsRoff(t, options)).join("");
157
162
  const escaped = escapeLineStarts(joined);
158
163
  return escaped.replace(/\n\n+/g, "\n.PP\n");
159
164
  }
package/dist/roff.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_roff = require('./roff-Cl0vekdg.cjs');
1
+ const require_roff = require('./roff-DJ-LkRXl.cjs');
2
2
 
3
3
  exports.escapeHyphens = require_roff.escapeHyphens;
4
4
  exports.escapeQuotedValue = require_roff.escapeQuotedValue;
package/dist/roff.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-C_87xXP6.cjs";
2
- export { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
1
+ import { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-COeDIWWF.cjs";
2
+ export { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
package/dist/roff.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-DlmeL3KB.js";
2
- export { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
1
+ import { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-CgYQDHBN.js";
2
+ export { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
package/dist/roff.js CHANGED
@@ -1,3 +1,3 @@
1
- import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-CwBRpWCo.js";
1
+ import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-CCdIQO7B.js";
2
2
 
3
3
  export { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/man",
3
- "version": "1.0.0-dev.921+754748bd",
3
+ "version": "1.0.0",
4
4
  "description": "Man page generator for Optique CLI parsers",
5
5
  "keywords": [
6
6
  "CLI",
@@ -84,8 +84,8 @@
84
84
  "optique-man": "./dist/cli.js"
85
85
  },
86
86
  "dependencies": {
87
- "@optique/core": "1.0.0-dev.921+754748bd",
88
- "@optique/run": "1.0.0-dev.921+754748bd"
87
+ "@optique/run": "1.0.0",
88
+ "@optique/core": "1.0.0"
89
89
  },
90
90
  "devDependencies": {
91
91
  "@types/node": "^20.19.9",