Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add man-page generator #125

Merged
merged 2 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ CLI tool to generate API documentation of a Node.js project.
Options:
-i, --input [patterns...] Specify input file patterns using glob syntax
-o, --output <path> Specify the relative or absolute output directory
-v, --version <semver> Specify the target version of Node.js, semver compliant (default: "v22.9.0")
-c, --changelog <url> Specify the path (file: or https://) to the CHANGELOG.md file (default:
"https://raw.githubusercontent.com/nodejs/node/HEAD/CHANGELOG.md")
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "mandoc")
-v, --version <semver> Specify the target version of Node.js, semver compliant (default: "v22.6.0")
-c, --changelog <url> Specify the path (file: or https://) to the CHANGELOG.md file (default: "https://raw.githubusercontent.com/nodejs/node/HEAD/CHANGELOG.md")
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "man-page")
-h, --help display help for command
```
71 changes: 42 additions & 29 deletions src/generators/man-page/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,56 @@ export default {
dependsOn: 'ast',

async generate(input, options) {
// Filter to only 'cli'.
const components = input.filter(({ api }) => api === 'cli');
if (!components.length) {
throw new Error('CLI.md not found');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could have been a more laid-out error message

}

// Find the appropriate headers
const optionsStart = input.findIndex(({ slug }) => slug === 'options');
const environmentStart = input.findIndex(
const optionsStart = components.findIndex(({ slug }) => slug === 'options');
const environmentStart = components.findIndex(
({ slug }) => slug === 'environment-variables-1'
Copy link
Member

@ovflowd ovflowd Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be a constant (including the options one above) and reference where from the CLI.md source we are looking for this.

If that file ever gets updated with this content removed, we're screwed.

);
// The first header that is <3 in depth after environmentStart
const environmentEnd = components.findIndex(
({ heading }, index) => heading.depth < 3 && index > environmentStart
);

if (optionsStart + environmentStart <= 0) {
throw new Error('Could not find headers');
}

// Generate the option mandoc
let optionsOutput = '';
for (let i = optionsStart + 1; i < environmentStart; i++) {
const el = input[i];
if (el.heading.depth === 3) {
optionsOutput += convertOptionToMandoc(el);
}
}

// Generate the environment mandoc
let envOutput = '';
for (let i = environmentStart + 1; i < input.length; i++) {
const el = input[i];
if (el.heading.depth === 3) {
envOutput += convertEnvVarToMandoc(el);
}
if (el.heading.depth < 3) break;
}
const output = {
// Extract the CLI options.
options: extractMandoc(
components,
optionsStart + 1,
environmentStart,
convertOptionToMandoc
),
// Extract the environment variables.
env: extractMandoc(
components,
environmentStart + 1,
environmentEnd,
convertEnvVarToMandoc
),
};

const apiTemplate = await readFile(
const template = await readFile(
join(import.meta.dirname, 'template.1'),
'utf-8'
);
const template = apiTemplate
.replace('__OPTIONS__', optionsOutput)
.replace('__ENVIRONMENT__', envOutput);

await writeFile(options.output, template);
const filledTemplate = template
.replace('__OPTIONS__', output.options)
.replace('__ENVIRONMENT__', output.env);

await writeFile(options.output, filledTemplate);
},
};

function extractMandoc(components, start, end, convert) {
return components
.slice(start, end)
.filter(({ heading }) => heading.depth === 3)
.map(convert)
.join('');
}
43 changes: 28 additions & 15 deletions src/generators/man-page/utils/converter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,33 @@ export function convertNodeToMandoc(node, isListItem = false) {
* @returns {string} The Mandoc formatted representation of the flag and its value.
*/
export function flagValueToMandoc(flag) {
// The seperator is '=' or ' '.
const sep = flag.match(/[= ]/)?.[0];
if (sep == null) return '';
// Split the flag into the name and value based on = or space delimiter.
// The separator is '=' or ' '.
let sep = flag.match(/[= ]/)?.[0];

if (sep == null) {
// This flag does not have a default value.
return '';
}

// Split the flag into the name and value based on the separator ('=' or space).
const value = flag.split(sep)[1];
// Format the value using Ns and Ar macros for Mandoc, if present.
// If the seperator is ' ', it'll become ''.
return value
? `${sep === ' ' ? '' : ' Ns = Ns'} Ar ${value.replace(/\]$/, '')}`
: '';

// If there is no value, return an empty string.
if (!value) {
return '';
}

// Determine the prefix based on the separator type.
const prefix = sep === ' ' ? '' : ' Ns = Ns';

// Combine prefix and formatted value.
return `${prefix} Ar ${value.replace(/\]$/, '')}`;
}

const formatFlag = flag =>
// 'Fl' denotes a flag, followed by an optional 'Ar' (argument).
`Fl ${flag.split(/[= ]/)[0].slice(1)}${flagValueToMandoc(flag)}`;

/**
* Converts an API option metadata entry into the Mandoc format.
* This function formats command-line options, including flags and descriptions,
Expand All @@ -94,17 +109,15 @@ export function convertOptionToMandoc(element) {
const formattedFlags = element.heading.data.text
.replace(/`/g, '')
.split(', ')
.map(
// 'Fl' denotes a flag
flag => `Fl ${flag.split(/[= ]/)[0].slice(1)}${flagValueToMandoc(flag)}`
)
.join(' , ');
.map(formatFlag)
.join(' , ')
.trim();

// Remove the header itself.
element.content.children.shift();

// Return the formatted flags and content, separated by Mandoc markers.
RedYetiDev marked this conversation as resolved.
Show resolved Hide resolved
return `.It ${formattedFlags.trim()}\n${convertNodeToMandoc(element.content)}\n.\n`;
return `.It ${formattedFlags}\n${convertNodeToMandoc(element.content).trim()}\n.\n`;
}

/**
Expand Down
Loading