@optique/core
Version:
Type-safe combinatorial command-line interface parser
112 lines (110 loc) • 3.87 kB
JavaScript
import { formatMessage } from "./message.js";
import { formatUsage, formatUsageTerm } from "./usage.js";
//#region src/doc.ts
/**
* Formats a documentation page into a human-readable string.
*
* This function takes a structured {@link DocPage} and converts it into
* a formatted string suitable for display in terminals or documentation.
* The formatting includes proper indentation, alignment, and optional
* color support.
*
* @param programName The name of the program, used in usage lines
* @param page The documentation page to format
* @param options Formatting options to customize the output
* @returns A formatted string representation of the documentation page
*
* @example
* ```typescript
* const page: DocPage = {
* brief: "A CLI tool",
* usage: [{ type: "literal", value: "myapp" }],
* sections: [{
* title: "Options",
* entries: [{
* term: { type: "option", short: "-v", long: "--verbose" },
* description: "Enable verbose output"
* }]
* }]
* };
*
* const formatted = formatDocPage("myapp", page, { colors: true });
* console.log(formatted);
* ```
*/
function formatDocPage(programName, page, options = {}) {
const termIndent = options.termIndent ?? 2;
const termWidth = options.termWidth ?? 26;
let output = "";
if (page.brief != null) {
output += formatMessage(page.brief, {
colors: options.colors,
maxWidth: options.maxWidth,
quotes: !options.colors
});
output += "\n";
}
if (page.usage != null) {
output += "Usage: ";
output += indentLines(formatUsage(programName, page.usage, {
colors: options.colors,
maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 7,
expandCommands: true
}), 7);
output += "\n";
}
if (page.description != null) {
output += "\n";
output += formatMessage(page.description, {
colors: options.colors,
maxWidth: options.maxWidth,
quotes: !options.colors
});
output += "\n";
}
const sections = page.sections.toSorted((a, b) => a.title == null && b.title == null ? 0 : a.title == null ? -1 : 1);
for (const section of sections) {
output += "\n";
if (section.title != null) output += `${section.title}:\n`;
for (const entry of section.entries) {
const term = formatUsageTerm(entry.term, {
colors: options.colors,
optionsSeparator: ", ",
maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - termIndent
});
let description = entry.description == null ? "" : formatMessage(entry.description, {
colors: options.colors,
quotes: !options.colors,
maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - termIndent - termWidth - 2
});
if (options.showDefault && entry.default != null) {
const prefix = typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [";
const suffix = typeof options.showDefault === "object" ? options.showDefault.suffix ?? "]" : "]";
const defaultText = `${prefix}${entry.default}${suffix}`;
const formattedDefault = options.colors ? `\x1b[2m${defaultText}\x1b[0m` : defaultText;
description += formattedDefault;
}
output += `${" ".repeat(termIndent)}${ansiAwareRightPad(term, termWidth)} ${description === "" ? "" : indentLines(description, termIndent + termWidth + 2)}\n`;
}
}
if (page.footer != null) {
output += "\n";
output += formatMessage(page.footer, {
colors: options.colors,
maxWidth: options.maxWidth,
quotes: !options.colors
});
}
return output;
}
function indentLines(text, indent) {
return text.split("\n").join("\n" + " ".repeat(indent));
}
function ansiAwareRightPad(text, length, char = " ") {
const ansiEscapeCodeRegex = /\x1B\[[0-9;]*[a-zA-Z]/g;
const strippedText = text.replace(ansiEscapeCodeRegex, "");
if (strippedText.length >= length) return text;
return text + char.repeat(length - strippedText.length);
}
//#endregion
export { formatDocPage };