Skip to content

Commit

Permalink
docs: improvements for generated documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
lowlighter committed Jan 15, 2022
1 parent 72c736c commit 696041e
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 112 deletions.
1 change: 0 additions & 1 deletion .github/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,5 @@ function testcase(name, env, args) {
Object.assign(result.with, {use_mocked_data:"yes", verify:"yes"})
}

console.log(arguments, result)
return result
}
219 changes: 108 additions & 111 deletions source/app/metrics/metadata.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default async function metadata({log = true, diff = false} = {}) {
if (!(await fs.promises.lstat(path.join(__plugins, name))).isDirectory())
continue
logger(`metrics/metadata > loading plugin metadata [${name}]`)
Plugins[name] = await metadata.plugin({__plugins, name, logger})
Plugins[name] = await metadata.plugin({__plugins, __templates, name, logger})
}
//Reorder keys
const {base, core, ...plugins} = Plugins //eslint-disable-line no-unused-vars
Expand Down Expand Up @@ -70,7 +70,7 @@ export default async function metadata({log = true, diff = false} = {}) {
}

/**Metadata extractor for templates */
metadata.plugin = async function({__plugins, name, logger}) {
metadata.plugin = async function({__plugins, __templates, name, logger}) {
try {
//Load meta descriptor
const raw = `${await fs.promises.readFile(path.join(__plugins, name, "metadata.yml"), "utf-8")}`
Expand Down Expand Up @@ -266,7 +266,21 @@ metadata.plugin = async function({__plugins, name, logger}) {
{
//Extract demos
const raw = `${await fs.promises.readFile(path.join(__plugins, name, "README.md"), "utf-8")}`
const demo = raw.match(/(?<demo><table>[\s\S]*?<[/]table>)/)?.groups?.demo?.replace(/<[/]?(?:table|tr)>/g, "")?.trim() ?? "<td></td>"
const demo = meta.examples ? demos({examples:meta.examples}) : raw.match(/(?<demo><table>[\s\S]*?<[/]table>)/)?.groups?.demo?.replace(/<[/]?(?:table|tr)>/g, "")?.trim() ?? "<td></td>"

//Compatibility
const templates = {}
const compatibility = {}
for (const template of await fs.promises.readdir(__templates)) {
if (!(await fs.promises.lstat(path.join(__templates, template))).isDirectory())
continue
templates[template] = yaml.load(`${await fs.promises.readFile(path.join(__templates, template, "metadata.yml"), "utf-8")}`)
const partials = path.join(__templates, template, "partials")
if ((fs.existsSync(partials)) && ((await fs.promises.lstat(partials)).isDirectory())) {
const supported = [...await fs.promises.readdir(partials)]
compatibility[template] = !!supported.filter(id => id.match(new RegExp(`^${name}(?:[.][\s\S]+)?[.]ejs$`))).length
}
}

//Header table
const header = [
Expand All @@ -275,100 +289,81 @@ metadata.plugin = async function({__plugins, name, logger}) {
` <tr><td colspan="2" align="center">${(meta.description ?? "").replaceAll("\n", "<br>")}</td></tr>`,
" <tr>",
' <th rowspan="3">Supported features<br><sub><a href="metadata.yml">→ Full specification</a></sub></th>',
//` <td>${Object.entries(compatibility).filter(([_, value]) => value).map(([id]) => `<a href="/source/plugins/${id}" title="${plugins[id].name}">${plugins[id].icon}</a>`).join(" ")}${meta.formats?.includes("markdown") ? " <code>✓ embed()</code>" : ""}</td>`,
` <td>${Object.entries(compatibility).filter(([_, value]) => value).map(([id]) => `<a href="/source/templates/${id}"><code>${templates[id].name ?? ""}</code></a>`).join(" ")}</td>`,
" </tr>",
" <tr>",
` <td>${[
meta.supports?.includes("user") ? "👤 Users" : "",
meta.supports?.includes("organization") ? "👥 Organizations" : "",
meta.supports?.includes("repository") ? "📓 Repositories" : ""
].filter(v => v).join(", ")}</td>`,
meta.supports?.includes("user") ? "<code>👤 Users</code>" : "",
meta.supports?.includes("organization") ? "<code>👥 Organizations</code>" : "",
meta.supports?.includes("repository") ? "<code>📓 Repositories</code>" : ""
].filter(v => v).join(" ")}</td>`,
" </tr>",
" <tr>",
` <td>${[...(meta.scopes ?? []).map(scope => `🔑 ${scope}`), ...Object.entries(inputs).filter(([_, {type}]) => type === "token").map(([token]) => `<code>🗝️ ${token}</code>`)].join(", ")}</td>`,
` <td>${[
...(meta.scopes ?? []).map(scope => `<code>🔑 ${{public_access:"(scopeless)"}[scope] ?? scope}</code>`),
...Object.entries(inputs).filter(([_, {type}]) => type === "token").map(([token]) => `<code>🗝️ ${token}</code>`),
...(meta.scopes?.length ? ["read:org", "read:user", "repo"].map(scope => !meta.scopes.includes(scope) ? `<code>${scope} (optional)</code>` : null).filter(v => v) : [])
].join(" ")}</td>`,
" </tr>",
" <tr>",
demos({colspan:2, examples:meta.examples}),
" </tr>",
"</table>"
].join("\n")

//Options table
let flags = new Set()
const table = [
"| Option | Type *(format)* **[default]** *{allowed values}* | Description |",
"| ------ | -------------------------------- | ----------- |",
"<table>",
" <tr>",
' <td align="center" nowrap="nowrap">Type</i></td><td align="center" nowrap="nowrap">Description</td>',
" </tr>",
Object.entries(inputs).map(([option, {description, type, ...o}]) => {
let row = []
{
let cell = []
if (o.required) {
cell.push("✔️")
flags.add("required")
}
if (type === "token") {
cell.push("🔐")
flags.add("secret")
}
if (o.inherits) {
cell.push("⏩")
flags.add("inherits")
}
if (o.global) {
cell.push("⏭️")
flags.add("global")
}
if (o.testing) {
cell.push("🔧")
flags.add("testing")
}
if (!Object.keys(previous?.inputs ?? {}).includes(option)) {
cell.push("✨")
flags.add("beta")
}
if (o.extras) {
cell.push("🧰")
flags.add("extras")
}
cell = cell.map(flag => `<sup>${flag}</sup>`)
cell.unshift(`${"`"}${option}${"`"}`)
row.push(cell.join(" "))
}
{
const cell = [`${"`"}${type}${"`"}`]
if ("format" in o)
cell.push(`*(${Array.isArray(o.format) ? o.format[0] : o.format})*`)
if ("default" in o) {
let text = o.default
if (o.default === ".user.login")
text = "*→ User login*"
if (o.default === ".user.twitter")
text = "*→ User attached twitter*"
if (o.default === ".user.website")
text = "*→ User attached website*"
cell.push(`**[${text}]**`)
}
if ("values" in o)
cell.push(`*{${o.values.map(value => `"${value}"`).join(", ")}}*`)
if ("min" in o)
cell.push(`*{${o.min} ≤`)
if (("min" in o)||("max" in o))
cell.push(`${"min" in o ? "" : "*{"}𝑥${"max" in o ? "" : "}*"}`)
if ("max" in o)
cell.push(`≤ ${o.max}}*`)
row.push(cell.join(" "))
const cell = []
if (o.required)
cell.push("✔️ Required<br>")
if (type === "token")
cell.push("🔐 Token<br>")
if (o.inherits)
cell.push(`⏩ Inherits <code>${o.inherits}</code><br>`)
if (o.global)
cell.push("⏭️ Global option<br>")
if (o.testing)
cell.push("🔧 For development")
if (!Object.keys(previous?.inputs ?? {}).includes(option))
cell.push("✨ On <code>master</code>/<code>main</code><br>")
if (o.extras)
cell.push("🌐 Web instances must configure <code>settings.json</code><br>")
cell.push(`<b>type:</b> <code>${type}</code>`)
if ("format" in o)
cell.push(`<i>(${Array.isArray(o.format) ? o.format[0] : o.format})</i>`)
cell.push("<br>")
if ("min" in o)
cell.push(`<i>(${o.min} ≤`)
if (("min" in o)||("max" in o))
cell.push(`${"min" in o ? "" : "<i>("}𝑥${"max" in o ? "" : ")</i>"}`)
if ("max" in o)
cell.push(`≤ ${o.max})</i>`)
if (("default" in o)&&(o.default !== "")) {
let text = o.default
if (o.default === ".user.login")
text = "<code>→ User login</code>"
if (o.default === ".user.twitter")
text = "<code>→ User attached twitter</code>"
if (o.default === ".user.website")
text = "<code>→ User attached website</code>"
cell.push(`<b>default:</b> ${text}<br>`)
}
row.push(description)
return `| ${row.join(" | ")} |`
if ("values" in o)
cell.push(`<b>allowed values:</b><ul>${o.values.map(value => `<li>${value}</li>`).join("")}</ul>`)
return ` <tr>
<td nowrap="nowrap"><code>${option}</code></td>
<td rowspan="2">${description}<img width="900" height="1" alt=""></td>
</tr>
<tr>
<td nowrap="nowrap">${cell.join("\n")}</td>
</tr>`
}).join("\n"),
"\n",
flags.size ? "Legend for option icons:" : "",
flags.has("required") ? "* ✔️ Value must be provided" : "",
flags.has("secret") ? "* 🔐 Value should be stored in repository secrets" : "",
flags.has("inherits") ? "* ⏩ Value inherits from its related global-level option" : "",
flags.has("global") ? "* ⏭️ Value be inherited by its related plugin-level option" : "",
flags.has("testing") ? "* 🔧 For development purposes, use with caution" : "",
flags.has("beta") ? "* ✨ Currently in beta-testing on `master`/`main`" : "",
flags.has("extras") ? "* 🧰 Must be enabled in `settings.json` (for web instances)" : "",
"</table>",
].flat(Infinity).filter(s => s).join("\n")

//Readme descriptor
Expand Down Expand Up @@ -405,23 +400,6 @@ metadata.template = async function({__templates, name, plugins, logger}) {
}
}

//Demo for main and individual readmes
function demo({colspan = null} = {}) { //eslint-disable-line no-inner-declarations
return [
` <td ${colspan ? `colspan="${colspan}"` : ""} align="center">`,
`${Object.entries(meta.examples ?? {}).map(([text, link]) => {
let img = `<img src="${link}" alt=""></img>`
if (text !== "default") {
const open = text.charAt(0) === "+" ? " open" : ""
img = `<details><summary${open}>${open ? text.substring(1) : text}</summary>${img}</details>`
}
return ` ${img}`
}).join("\n")}`,
' <img width="900" height="1" alt="">',
" </td>"
].join("\n")
}

//Header table
const header = [
"<table>",
Expand All @@ -433,23 +411,23 @@ metadata.template = async function({__templates, name, plugins, logger}) {
" </tr>",
" <tr>",
` <td>${[
meta.supports?.includes("user") ? "👤 Users" : "",
meta.supports?.includes("organization") ? "👥 Organizations" : "",
meta.supports?.includes("repository") ? "📓 Repositories" : ""
].filter(v => v).join(", ")}</td>`,
meta.supports?.includes("user") ? "<code>👤 Users</code>" : "",
meta.supports?.includes("organization") ? "<code>👥 Organizations</code>" : "",
meta.supports?.includes("repository") ? "<code>📓 Repositories</code>" : ""
].filter(v => v).join(" ")}</td>`,
" </tr>",
" <tr>",
` <td>${[
meta.formats?.includes("svg") ? "*️⃣ SVG" : "",
meta.formats?.includes("png") ? "*️⃣ PNG" : "",
meta.formats?.includes("jpeg") ? "*️⃣ JPEG" : "",
meta.formats?.includes("json") ? "#️⃣ JSON" : "",
meta.formats?.includes("markdown") ? "🔠 Markdown" : "",
meta.formats?.includes("markdown-pdf") ? "🔠 Markdown (PDF)" : "",
].filter(v => v).join(", ")}</td>`,
meta.formats?.includes("svg") ? "<code>*️⃣ SVG</code>" : "",
meta.formats?.includes("png") ? "<code>*️⃣ PNG</code>" : "",
meta.formats?.includes("jpeg") ? "<code>*️⃣ JPEG</code>" : "",
meta.formats?.includes("json") ? "<code>#️⃣ JSON</code>" : "",
meta.formats?.includes("markdown") ? "<code>🔠 Markdown</code>" : "",
meta.formats?.includes("markdown-pdf") ? "<code>🔠 Markdown (PDF)</code>" : "",
].filter(v => v).join(" ")}</td>`,
" </tr>",
" <tr>",
demo({colspan:2}),
demos({colspan:2, examples:meta.examples}),
" </tr>",
"</table>"
].join("\n")
Expand All @@ -462,7 +440,7 @@ metadata.template = async function({__templates, name, plugins, logger}) {
formats:meta.formats ?? null,
supports:meta.supports ?? null,
readme:{
demo:demo(),
demo:demos({examples:meta.examples}),
compatibility:{
...Object.fromEntries(Object.entries(compatibility).filter(([_, value]) => value)),
...Object.fromEntries(Object.entries(compatibility).filter(([_, value]) => !value).map(([key, value]) => [key, meta.formats?.includes("markdown") ? "embed" : value])),
Expand Down Expand Up @@ -496,3 +474,22 @@ metadata.to = {
return name ? key.replace(new RegExp(`^(${name}.)`, "g"), "") : key
},
}

//Demo for main and individual readmes
function demos({colspan = null, examples = {}} = {}) {
return [
` <td ${colspan ? `colspan="${colspan}"` : ""} align="center">`,
`${Object.entries(examples).map(([text, link]) => {
let img = `<img src="${link}" alt=""></img>`
if (text !== "default") {
const open = text.charAt(0) === "+" ? " open" : ""
text = open ? text.substring(1) : text
text = `${text.charAt(0).toLocaleUpperCase()}${text.substring(1)}`
img = `<details${open}><summary>${text}</summary>${img}</details>`
}
return ` ${img}`
}).join("\n")}`,
' <img width="900" height="1" alt="">',
" </td>"
].join("\n")
}

0 comments on commit 696041e

Please sign in to comment.