@optique/man 0.10.0-dev.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.
@@ -0,0 +1,122 @@
1
+ //#region src/roff.ts
2
+ /**
3
+ * Escapes backslashes in text for roff.
4
+ * This is an internal helper that only handles backslash escaping.
5
+ *
6
+ * @param text The text to escape.
7
+ * @returns The text with backslashes escaped.
8
+ */
9
+ function escapeBackslashes(text) {
10
+ return text.replace(/\\/g, "\\\\");
11
+ }
12
+ /**
13
+ * Escapes period and single quote at line starts.
14
+ * In roff, these characters have special meaning when at the start of a line.
15
+ *
16
+ * @param text The text to process.
17
+ * @returns The text with line-start special characters escaped.
18
+ */
19
+ function escapeLineStarts(text) {
20
+ if (text === "") return "";
21
+ let result = text;
22
+ if (result.startsWith(".") || result.startsWith("'")) result = "\\&" + result;
23
+ result = result.replace(/\n([.'])/g, "\n\\&$1");
24
+ return result;
25
+ }
26
+ /**
27
+ * Escapes special roff characters in plain text.
28
+ *
29
+ * This function handles the following escapes:
30
+ * - Backslash (`\`) → `\\`
31
+ * - Period (`.`) at line start → `\&.`
32
+ * - Single quote (`'`) at line start → `\&'`
33
+ *
34
+ * @param text The plain text to escape.
35
+ * @returns The escaped text safe for use in roff documents.
36
+ * @since 0.10.0
37
+ */
38
+ function escapeRoff(text) {
39
+ if (text === "") return "";
40
+ return escapeLineStarts(escapeBackslashes(text));
41
+ }
42
+ /**
43
+ * Escapes hyphens in option names to prevent line breaks.
44
+ *
45
+ * In roff, a regular hyphen (`-`) can be used as a line break point.
46
+ * For option names like `--verbose`, we want to use `\-` which prevents
47
+ * line breaks and renders as a proper minus sign.
48
+ *
49
+ * @param text The text containing hyphens to escape.
50
+ * @returns The text with hyphens escaped as `\-`.
51
+ * @since 0.10.0
52
+ */
53
+ function escapeHyphens(text) {
54
+ return text.replace(/-/g, "\\-");
55
+ }
56
+ /**
57
+ * Formats a single {@link MessageTerm} as roff markup.
58
+ * Note: This does NOT escape line-start characters (. and ') because
59
+ * these terms may be concatenated with other terms. Line-start escaping
60
+ * is done in formatMessageAsRoff after all terms are joined.
61
+ *
62
+ * @param term The message term to format.
63
+ * @returns The roff-formatted string.
64
+ */
65
+ function formatTermAsRoff(term) {
66
+ switch (term.type) {
67
+ case "text": return escapeBackslashes(term.text);
68
+ case "optionName": return `\\fB${escapeHyphens(term.optionName)}\\fR`;
69
+ case "optionNames": return term.optionNames.map((name) => `\\fB${escapeHyphens(name)}\\fR`).join(", ");
70
+ case "metavar": return `\\fI${escapeBackslashes(term.metavar)}\\fR`;
71
+ case "value": return `"${escapeBackslashes(term.value)}"`;
72
+ case "values":
73
+ if (term.values.length === 0) return "";
74
+ return term.values.map((v) => `"${escapeBackslashes(v)}"`).join(" ");
75
+ case "envVar": return `\\fB${escapeBackslashes(term.envVar)}\\fR`;
76
+ case "commandLine": return `\\fB${escapeHyphens(escapeBackslashes(term.commandLine))}\\fR`;
77
+ default: {
78
+ const _exhaustive = term;
79
+ throw new TypeError(`Unknown message term type: ${_exhaustive.type}.`);
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Formats a {@link Message} as roff markup for use in man pages.
85
+ *
86
+ * This function converts Optique's structured message format into roff
87
+ * markup suitable for man pages. Each message term type is converted
88
+ * to appropriate roff formatting:
89
+ *
90
+ * | Term Type | Roff Output |
91
+ * |-----------|-------------|
92
+ * | `text` | Plain text (escaped) |
93
+ * | `optionName` | `\fB--option\fR` (bold) |
94
+ * | `optionNames` | `\fB--opt1\fR, \fB-o\fR` (comma-separated bold) |
95
+ * | `metavar` | `\fIFILE\fR` (italic) |
96
+ * | `value` | `"value"` (quoted) |
97
+ * | `values` | `"a" "b" "c"` (space-separated quoted) |
98
+ * | `envVar` | `\fBVAR\fR` (bold) |
99
+ * | `commandLine` | `\fBcmd\fR` (bold) |
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * import { formatMessageAsRoff } from "@optique/man/roff";
104
+ * import { message, optionName, metavar } from "@optique/core/message";
105
+ *
106
+ * const msg = message`Use ${optionName("--config")} ${metavar("FILE")}`;
107
+ * const roff = formatMessageAsRoff(msg);
108
+ * // => "Use \\fB\\-\\-config\\fR \\fIFILE\\fR"
109
+ * ```
110
+ *
111
+ * @param msg The message to format.
112
+ * @returns The roff-formatted string.
113
+ * @since 0.10.0
114
+ */
115
+ function formatMessageAsRoff(msg) {
116
+ const joined = msg.map(formatTermAsRoff).join("");
117
+ const escaped = escapeLineStarts(joined);
118
+ return escaped.replace(/\n\n+/g, "\n.PP\n");
119
+ }
120
+
121
+ //#endregion
122
+ export { escapeHyphens, escapeRoff, formatMessageAsRoff };
package/dist/roff.cjs ADDED
@@ -0,0 +1,5 @@
1
+ const require_roff = require('./roff-DzxoXJIL.cjs');
2
+
3
+ exports.escapeHyphens = require_roff.escapeHyphens;
4
+ exports.escapeRoff = require_roff.escapeRoff;
5
+ exports.formatMessageAsRoff = require_roff.formatMessageAsRoff;
@@ -0,0 +1,2 @@
1
+ import { escapeHyphens, escapeRoff, formatMessageAsRoff } from "./roff-Cs3_UBG5.cjs";
2
+ export { escapeHyphens, escapeRoff, formatMessageAsRoff };
package/dist/roff.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { escapeHyphens, escapeRoff, formatMessageAsRoff } from "./roff-CK3-yKaY.js";
2
+ export { escapeHyphens, escapeRoff, formatMessageAsRoff };
package/dist/roff.js ADDED
@@ -0,0 +1,3 @@
1
+ import { escapeHyphens, escapeRoff, formatMessageAsRoff } from "./roff-i3JSYqke.js";
2
+
3
+ export { escapeHyphens, escapeRoff, formatMessageAsRoff };
package/package.json ADDED
@@ -0,0 +1,104 @@
1
+ {
2
+ "name": "@optique/man",
3
+ "version": "0.10.0-dev.0",
4
+ "description": "Man page generator for Optique CLI parsers",
5
+ "keywords": [
6
+ "CLI",
7
+ "command-line",
8
+ "commandline",
9
+ "parser",
10
+ "man",
11
+ "manpage",
12
+ "documentation",
13
+ "roff",
14
+ "groff"
15
+ ],
16
+ "license": "MIT",
17
+ "author": {
18
+ "name": "Hong Minhee",
19
+ "email": "hong@minhee.org",
20
+ "url": "https://hongminhee.org/"
21
+ },
22
+ "homepage": "https://optique.dev/",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/dahlia/optique.git",
26
+ "directory": "packages/man/"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/dahlia/optique/issues"
30
+ },
31
+ "funding": [
32
+ "https://github.com/sponsors/dahlia"
33
+ ],
34
+ "engines": {
35
+ "node": ">=20.0.0",
36
+ "bun": ">=1.2.0",
37
+ "deno": ">=2.3.0"
38
+ },
39
+ "files": [
40
+ "dist/",
41
+ "package.json",
42
+ "README.md"
43
+ ],
44
+ "type": "module",
45
+ "module": "./dist/index.js",
46
+ "main": "./dist/index.cjs",
47
+ "types": "./dist/index.d.ts",
48
+ "exports": {
49
+ ".": {
50
+ "types": {
51
+ "import": "./dist/index.d.ts",
52
+ "require": "./dist/index.d.cts"
53
+ },
54
+ "import": "./dist/index.js",
55
+ "require": "./dist/index.cjs"
56
+ },
57
+ "./roff": {
58
+ "types": {
59
+ "import": "./dist/roff.d.ts",
60
+ "require": "./dist/roff.d.cts"
61
+ },
62
+ "import": "./dist/roff.js",
63
+ "require": "./dist/roff.cjs"
64
+ },
65
+ "./man": {
66
+ "types": {
67
+ "import": "./dist/man.d.ts",
68
+ "require": "./dist/man.d.cts"
69
+ },
70
+ "import": "./dist/man.js",
71
+ "require": "./dist/man.cjs"
72
+ },
73
+ "./cli": {
74
+ "types": {
75
+ "import": "./dist/cli.d.ts",
76
+ "require": "./dist/cli.d.cts"
77
+ },
78
+ "import": "./dist/cli.js",
79
+ "require": "./dist/cli.cjs"
80
+ }
81
+ },
82
+ "sideEffects": false,
83
+ "bin": {
84
+ "optique-man": "./dist/cli.js"
85
+ },
86
+ "dependencies": {
87
+ "@optique/core": "",
88
+ "@optique/run": ""
89
+ },
90
+ "devDependencies": {
91
+ "@types/node": "^20.19.9",
92
+ "tsdown": "^0.13.0",
93
+ "tsx": "^4.21.0",
94
+ "typescript": "^5.8.3"
95
+ },
96
+ "scripts": {
97
+ "build": "tsdown",
98
+ "prepublish": "tsdown",
99
+ "test": "tsdown && node --experimental-transform-types --test 'src/**/*.test.ts'",
100
+ "test:bun": "tsdown && bun test src/",
101
+ "test:deno": "deno test",
102
+ "test-all": "tsdown && node --experimental-transform-types --test 'src/**/*.test.ts' && bun test src/ && deno test"
103
+ }
104
+ }