Last active
July 10, 2020 20:16
-
-
Save jordanell/fd8d3871db75cdf32138ca50bc7beb10 to your computer and use it in GitHub Desktop.
A script to parse React components and generate markdown files for their API documentation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from "fs"; | |
import path from "path"; | |
import { parse } from "react-docgen"; | |
import buildMarkdown from "./buildMarkdown"; | |
async function generate(options = {}) { | |
const { component: componentObject } = options; | |
// Load source code | |
const src = fs.readFileSync(path.resolve(__dirname, "path/to/components/Button.jsx"), "utf8"); | |
// Get path object of the component | |
const pathObj = path.parse(componentObject.filename); | |
// Actually parse the react component | |
const reactAPI = parse(src, null, null, { | |
filename: 'Button.jsx' | |
}); | |
// Generate a markdown string to represent the component | |
const markdown = buildMarkdown(reactAPI); | |
const markdownDirectory = path.resolve( | |
__dirname, | |
'../src/pages/components-api' | |
); | |
// Write the markdown to the needed file | |
fs.writeFileSync( | |
path.resolve(markdownDirectory, 'button.md'), | |
markdown | |
); | |
} | |
generate(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { parse as parseDoctrine } from "doctrine"; | |
function escapeCell(value) { | |
// As the pipe is use for the table structure | |
return value | |
.replace(/</g, "<") | |
.replace(/`</g, "`<") | |
.replace(/\|/g, "\\|"); | |
} | |
function generatePropDescription(prop) { | |
const { description, type } = prop; | |
const parsed = parseDoctrine(description, { | |
sloppy: true | |
}); | |
// Two new lines result in a newline in the table. | |
// All other new lines must be eliminated to prevent markdown mayhem. | |
return escapeCell(parsed.description) | |
.replace(/(\r?\n){2}/g, "<br>") | |
.replace(/\r?\n/g, " "); | |
} | |
function generatePropType(type) { | |
switch (type.name) { | |
case "union": | |
case "enum": { | |
return ( | |
type.value | |
.map(type2 => { | |
if (type.name === "enum") { | |
return escapeCell(type2.value); | |
} | |
return generatePropType(type2); | |
}) | |
// Display one value per line as it's better for visibility. | |
.join("<br>| ") | |
); | |
} | |
default: | |
return type.name; | |
} | |
} | |
function generateProps(reactAPI) { | |
const header = "## Props"; | |
let text = `${header} | |
| Name | Type | Default | Description | | |
|:-----|:-----|:--------|:------------|\n`; | |
text = Object.keys(reactAPI.props).reduce((textProps, propRaw) => { | |
const prop = getProp(reactAPI.props[propRaw]; | |
const description = generatePropDescription(prop); | |
if (description === null) { | |
return textProps; | |
} | |
let defaultValue = ""; | |
if (prop.defaultValue) { | |
defaultValue = `<span class="prop-default">${escapeCell( | |
prop.defaultValue.value.replace(/\r*\n/g, "") | |
)}</span>`; | |
} | |
let propName = propRaw; | |
if (prop.required) { | |
propName = `<span class="prop-name required">${propRaw} *</span>`; | |
} else { | |
propName = `<span class="prop-name">${propRaw}</span>`; | |
} | |
return `${textProps}| ${propName} | <span class="prop-type">${generatePropType( | |
prop.type | |
)}</span> | ${defaultValue} | ${description} |\n`; | |
}, text); | |
text = `${text} | |
Any other props supplied will be provided to the root element.`; | |
return text; | |
} | |
export default function buildMarkdown(reactAPI) { | |
return [ | |
"<!--- This documentation is automatically generated, do not try to edit it. -->", | |
"", | |
`# Button API`, | |
"", | |
`<p class="description">The API documentation of the Button React component.</p>`, | |
"", | |
generateProps(reactAPI) | |
].join("\n"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment