Skip to content

Commit 6f5c34a

Browse files
committed
feat: add mandoc generator
1 parent f88ed7a commit 6f5c34a

File tree

6 files changed

+192
-4
lines changed

6 files changed

+192
-4
lines changed

.github/workflows/codespell.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ jobs:
1818
with:
1919
ignore_words_list: crate,raison
2020
exclude_file: .gitignore
21-
skip: package-lock.json
21+
skip: package-lock.json,./src/generators/mandoc/template.1

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ CLI tool to generate API documentation of a Node.js project.
3737
Options:
3838
-i, --input [patterns...] Specify input file patterns using glob syntax
3939
-o, --output <path> Specify the relative or absolute output directory
40-
-v, --version <semver> Specify the target version of Node.js, semver compliant (default: "v22.6.0")
40+
-v, --version <semver> Specify the target version of Node.js, semver compliant (default: "v22.9.0")
4141
-c, --changelog <url> Specify the path (file: or https://) to the CHANGELOG.md file (default:
4242
"https://raw.githubusercontent.com/nodejs/node/HEAD/CHANGELOG.md")
43-
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html",
44-
"legacy-html-all")
43+
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "mandoc")
4544
-h, --help display help for command
4645
```

src/generators/index.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import jsonSimple from './json-simple/index.mjs';
44
import legacyHtml from './legacy-html/index.mjs';
55
import legacyHtmlAll from './legacy-html-all/index.mjs';
6+
import mandoc from './mandoc/index.mjs';
67

78
export default {
89
'json-simple': jsonSimple,
910
'legacy-html': legacyHtml,
1011
'legacy-html-all': legacyHtmlAll,
12+
mandoc: mandoc,
1113
};

src/generators/mandoc/index.mjs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict';
2+
3+
import { optionToMandoc, envToMandoc } from './utils/converter.mjs';
4+
import { writeFile, readFile } from 'node:fs/promises';
5+
import { join } from 'node:path';
6+
7+
/**
8+
* This generator generates a mandoc version of the API docs
9+
*
10+
* @typedef {Array<ApiDocMetadataEntry>} Input
11+
*
12+
* @type {import('../types.d.ts').GeneratorMetadata<Input, string>}
13+
*/
14+
export default {
15+
name: 'mandoc',
16+
17+
version: '1.0.0',
18+
19+
description: 'Generates the `node.1` file.',
20+
21+
dependsOn: 'ast',
22+
23+
async generate(input, options) {
24+
// Find the appropriate headers
25+
const optionsStart = input.findIndex(({ slug }) => slug === 'options');
26+
const environmentStart = input.findIndex(
27+
({ slug }) => slug === 'environment-variables-1'
28+
);
29+
30+
// Generate the option mandoc
31+
let optionsOutput = '';
32+
for (let i = optionsStart + 1; i < environmentStart; i++) {
33+
const el = input[i];
34+
if (el.heading?.depth === 3) {
35+
optionsOutput += optionToMandoc(el);
36+
}
37+
}
38+
39+
// Generate the environment mandoc
40+
let envOutput = '';
41+
for (let i = environmentStart + 1; i < input.length; i++) {
42+
const el = input[i];
43+
if (el.heading?.depth === 3) {
44+
envOutput += envToMandoc(el);
45+
}
46+
if (el.heading?.depth < 3) break;
47+
}
48+
49+
const apiTemplate = await readFile(
50+
join(import.meta.dirname, 'template.1'),
51+
'utf-8'
52+
);
53+
const template = apiTemplate
54+
.replace('__OPTIONS__', optionsOutput)
55+
.replace('__ENVIRONMENT__', envOutput);
56+
57+
return await writeFile(options.output, template);
58+
},
59+
};

src/generators/mandoc/template.1

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
.Dd $Mdocdate$
2+
.Dt NODE 1
3+
.
4+
.Sh NAME
5+
.Nm node
6+
.Nd server-side JavaScript runtime
7+
.
8+
.Sh SYNOPSIS
9+
.Nm node
10+
.Op Ar options
11+
.Op Ar v8 options
12+
.Op Ar <program-entry-point> | Fl e Ar string | Fl -
13+
.Op Ar arguments ...
14+
.
15+
.Nm node
16+
.Cm inspect,
17+
.Op Ar <program-entry-point> | Fl e Ar string | Ar <host>:<port>
18+
.Ar ...
19+
.
20+
.Nm node
21+
.Op Fl -v8-options
22+
.
23+
.Sh DESCRIPTION
24+
Node.js is a set of libraries for JavaScript which allows it to be used outside of the browser.
25+
It is primarily focused on creating simple, easy-to-build network clients and servers.
26+
.Pp
27+
Execute
28+
.Nm
29+
without arguments to start a REPL.
30+
.
31+
.Sh OPTIONS
32+
.Bl -tag -width 6n
33+
__OPTIONS__
34+
.El
35+
.
36+
.Sh ENVIRONMENT
37+
.Bl -tag -width 6n
38+
__ENVIRONMENT__
39+
.El
40+
.
41+
.Sh BUGS
42+
Bugs are tracked in GitHub Issues:
43+
.Sy https://github.com/nodejs/node/issues
44+
.
45+
.Sh COPYRIGHT
46+
Copyright Node.js contributors.
47+
Node.js is available under the MIT license.
48+
.
49+
.Pp
50+
Node.js also includes external libraries that are available under a variety of licenses.
51+
See
52+
.Sy https://github.com/nodejs/node/blob/HEAD/LICENSE
53+
for the full license text.
54+
.
55+
.Sh SEE ALSO
56+
Website:
57+
.Sy https://nodejs.org/
58+
.
59+
.Pp
60+
Documentation:
61+
.Sy https://nodejs.org/api/
62+
.
63+
.Pp
64+
GitHub repository and issue tracker:
65+
.Sy https://github.com/nodejs/node
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
function toMandoc(node, opts) {
2+
const mapChildren = () => node.children.map(toMandoc).join('');
3+
const value = () => node.value.replace(/\\/g, '\\\\');
4+
5+
switch (node.type) {
6+
case 'root':
7+
return node.children.map(toMandoc).join('\n');
8+
case 'heading':
9+
return `.Sh ${mapChildren()}`;
10+
case 'link':
11+
case 'paragraph':
12+
case 'listItem':
13+
return `${opts.fromListParent ? '.It\n' : ''}${mapChildren()}`;
14+
case 'text':
15+
return value();
16+
case 'inlineCode':
17+
return `\\fB${value()}\\fR`; // Bold formatting
18+
case 'blockquote':
19+
return ''; // Ignore stability
20+
case 'code':
21+
return `.Bd -literal\n${value()}\n.Ed`;
22+
case 'list':
23+
return `.Bl -bullet\n${node.children.map(child => toMandoc(child, { fromListParent: true })).join('\n')}\n.El\n.\n`;
24+
case 'emphasis':
25+
return `\\fI${mapChildren()}\\fR`; // Italic formatting
26+
default:
27+
return '';
28+
}
29+
}
30+
31+
function flagToMandoc(flag) {
32+
const [, value] = flag.split(/[= ]/);
33+
const separator = flag.match(/[= ]/)?.[0];
34+
return separator === ' ' || !value
35+
? ''
36+
: ` Ns ${separator} Ns Ar ${value.replace(/\]$/, '')}`;
37+
}
38+
39+
export function optionToMandoc(element) {
40+
const formatFlag = flag => {
41+
const name = flag.split(/\[?[= ]/)[0].slice(1);
42+
return `Fl ${name}${flagToMandoc(flag)}`;
43+
};
44+
45+
const formattedFlags = element.heading.data.text
46+
.replace(/`/g, '')
47+
.split(', ')
48+
.map(formatFlag)
49+
.join(' , ');
50+
51+
element.content.children.shift(); // Remove the first child
52+
return `.It ${formattedFlags.trim()}\n${toMandoc(element.content)}\n.\n`;
53+
}
54+
55+
export function envToMandoc(element) {
56+
const [varName, varValue] = element.heading.data.text
57+
.replace(/`/g, '')
58+
.split('=');
59+
const value = varValue ? ` Ar ${varValue}` : '';
60+
61+
element.content.children.shift(); // Remove the first child
62+
return `.It Ev ${varName}${value}\n${toMandoc(element.content)}\n.\n`;
63+
}

0 commit comments

Comments
 (0)