@optique/man 0.10.7-dev.485 → 1.0.0-dev.1109

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.
@@ -1,218 +0,0 @@
1
- import { escapeHyphens, formatMessageAsRoff } from "./roff-C_MiRXVS.js";
2
-
3
- //#region src/man.ts
4
- /**
5
- * Formats a date for use in man pages.
6
- *
7
- * @param date The date to format, or undefined.
8
- * @returns The formatted date string, or undefined.
9
- * @since 0.10.0
10
- */
11
- function formatDateForMan(date) {
12
- if (date === void 0) return void 0;
13
- if (typeof date === "string") return date;
14
- const months = [
15
- "January",
16
- "February",
17
- "March",
18
- "April",
19
- "May",
20
- "June",
21
- "July",
22
- "August",
23
- "September",
24
- "October",
25
- "November",
26
- "December"
27
- ];
28
- return `${months[date.getMonth()]} ${date.getFullYear()}`;
29
- }
30
- function escapeThField(value) {
31
- return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
32
- }
33
- /**
34
- * Formats a single {@link UsageTerm} as roff markup for the SYNOPSIS section.
35
- *
36
- * @param term The usage term to format.
37
- * @returns The roff-formatted string.
38
- * @since 0.10.0
39
- */
40
- function formatUsageTermAsRoff(term) {
41
- if ("hidden" in term && term.hidden) return "";
42
- switch (term.type) {
43
- case "argument": return `\\fI${term.metavar}\\fR`;
44
- case "option": {
45
- const names = term.names.map((name) => `\\fB${escapeHyphens(name)}\\fR`).join(" | ");
46
- const metavarPart = term.metavar ? ` \\fI${term.metavar}\\fR` : "";
47
- return `[${names}${metavarPart}]`;
48
- }
49
- case "command": return `\\fB${term.name}\\fR`;
50
- case "optional": {
51
- const inner = formatUsageAsRoff(term.terms);
52
- return `[${inner}]`;
53
- }
54
- case "multiple": {
55
- const inner = formatUsageAsRoff(term.terms);
56
- if (term.min < 1) return `[${inner} ...]`;
57
- return `${inner} ...`;
58
- }
59
- case "exclusive": {
60
- const alternatives = term.terms.map((t) => formatUsageAsRoff(t)).join(" | ");
61
- return `(${alternatives})`;
62
- }
63
- case "literal": return term.value;
64
- case "passthrough": return "[...]";
65
- default: {
66
- const _exhaustive = term;
67
- throw new TypeError(`Unknown usage term type: ${_exhaustive.type}.`);
68
- }
69
- }
70
- }
71
- /**
72
- * Formats a {@link Usage} array as roff markup.
73
- *
74
- * @param usage The usage terms to format.
75
- * @returns The roff-formatted string.
76
- */
77
- function formatUsageAsRoff(usage) {
78
- return usage.map(formatUsageTermAsRoff).filter((s) => s !== "").join(" ");
79
- }
80
- /**
81
- * Formats a {@link DocEntry}'s term for man page output.
82
- *
83
- * @param term The usage term from the entry.
84
- * @returns The roff-formatted term string.
85
- */
86
- function formatDocEntryTerm(term) {
87
- switch (term.type) {
88
- case "option": {
89
- const names = term.names.map((name) => `\\fB${escapeHyphens(name)}\\fR`).join(", ");
90
- const metavarPart = term.metavar ? ` \\fI${term.metavar}\\fR` : "";
91
- return `${names}${metavarPart}`;
92
- }
93
- case "command": return `\\fB${term.name}\\fR`;
94
- case "argument": return `\\fI${term.metavar}\\fR`;
95
- case "literal": return term.value;
96
- default: return formatUsageTermAsRoff(term);
97
- }
98
- }
99
- /**
100
- * Formats a {@link DocSection} as roff markup with .TP macros.
101
- *
102
- * @param section The section to format.
103
- * @returns The roff-formatted section content.
104
- */
105
- function formatDocSectionEntries(section) {
106
- const lines = [];
107
- for (const entry of section.entries) {
108
- lines.push(".TP");
109
- lines.push(formatDocEntryTerm(entry.term));
110
- if (entry.description) {
111
- let desc = formatMessageAsRoff(entry.description);
112
- if (entry.default) desc += ` [${formatMessageAsRoff(entry.default)}]`;
113
- lines.push(desc);
114
- } else if (entry.default) lines.push(`[${formatMessageAsRoff(entry.default)}]`);
115
- }
116
- return lines.join("\n");
117
- }
118
- /**
119
- * Formats a {@link DocPage} as a complete man page in roff format.
120
- *
121
- * This function generates a man page following the standard man(7) format,
122
- * including sections for NAME, SYNOPSIS, DESCRIPTION, OPTIONS, and more.
123
- *
124
- * @example
125
- * ```typescript
126
- * import { formatDocPageAsMan } from "@optique/man/man";
127
- * import type { DocPage } from "@optique/core/doc";
128
- *
129
- * const page: DocPage = {
130
- * brief: message`A sample CLI application`,
131
- * usage: [{ type: "argument", metavar: "FILE" }],
132
- * sections: [],
133
- * };
134
- *
135
- * const manPage = formatDocPageAsMan(page, {
136
- * name: "myapp",
137
- * section: 1,
138
- * version: "1.0.0",
139
- * });
140
- * ```
141
- *
142
- * @param page The documentation page to format.
143
- * @param options The man page options.
144
- * @returns The complete man page in roff format.
145
- * @since 0.10.0
146
- */
147
- function formatDocPageAsMan(page, options) {
148
- const lines = [];
149
- const thParts = [escapeThField(options.name.toUpperCase()), options.section.toString()];
150
- const hasDate = options.date != null;
151
- const hasVersion = options.version != null;
152
- const hasManual = options.manual != null;
153
- if (hasDate) thParts.push(`"${escapeThField(formatDateForMan(options.date))}"`);
154
- else if (hasVersion || hasManual) thParts.push("\"\"");
155
- if (hasVersion) thParts.push(`"${escapeThField(`${options.name} ${options.version}`)}"`);
156
- else if (hasManual) thParts.push("\"\"");
157
- if (hasManual) thParts.push(`"${escapeThField(options.manual)}"`);
158
- lines.push(`.TH ${thParts.join(" ")}`);
159
- lines.push(".SH NAME");
160
- if (page.brief) lines.push(`${options.name} \\- ${formatMessageAsRoff(page.brief)}`);
161
- else lines.push(options.name);
162
- if (page.usage) {
163
- lines.push(".SH SYNOPSIS");
164
- lines.push(`.B ${options.name}`);
165
- const usageStr = formatUsageAsRoff(page.usage);
166
- if (usageStr) lines.push(usageStr);
167
- }
168
- if (page.description) {
169
- lines.push(".SH DESCRIPTION");
170
- lines.push(formatMessageAsRoff(page.description));
171
- }
172
- for (const section of page.sections) {
173
- if (section.entries.length === 0) continue;
174
- const title = section.title?.toUpperCase() ?? "OPTIONS";
175
- lines.push(`.SH ${title}`);
176
- lines.push(formatDocSectionEntries(section));
177
- }
178
- if (options.environment && options.environment.entries.length > 0) {
179
- lines.push(".SH ENVIRONMENT");
180
- lines.push(formatDocSectionEntries(options.environment));
181
- }
182
- if (options.files && options.files.entries.length > 0) {
183
- lines.push(".SH FILES");
184
- lines.push(formatDocSectionEntries(options.files));
185
- }
186
- if (options.exitStatus && options.exitStatus.entries.length > 0) {
187
- lines.push(".SH EXIT STATUS");
188
- lines.push(formatDocSectionEntries(options.exitStatus));
189
- }
190
- if (options.examples) {
191
- lines.push(".SH EXAMPLES");
192
- lines.push(formatMessageAsRoff(options.examples));
193
- }
194
- if (options.bugs) {
195
- lines.push(".SH BUGS");
196
- lines.push(formatMessageAsRoff(options.bugs));
197
- }
198
- if (options.seeAlso && options.seeAlso.length > 0) {
199
- lines.push(".SH SEE ALSO");
200
- const refs = options.seeAlso.map((ref, i) => {
201
- const suffix = i < options.seeAlso.length - 1 ? "," : "";
202
- return `.BR ${ref.name} (${ref.section})${suffix}`;
203
- });
204
- lines.push(refs.join("\n"));
205
- }
206
- if (options.author) {
207
- lines.push(".SH AUTHOR");
208
- lines.push(formatMessageAsRoff(options.author));
209
- }
210
- if (page.footer) {
211
- lines.push(".PP");
212
- lines.push(formatMessageAsRoff(page.footer));
213
- }
214
- return lines.join("\n");
215
- }
216
-
217
- //#endregion
218
- export { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff };