UNPKG

@optique/core

Version:

Type-safe combinatorial command-line interface parser

112 lines (110 loc) 3.87 kB
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 };