Created
September 30, 2022 23:10
-
-
Save kaosine/5c4a87b8c2efbd87eca8614a0ef655ce to your computer and use it in GitHub Desktop.
Conversion of atom json filter for api
This file contains 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
require("json"); | |
require("kramdown"); | |
class ApiJsonFilter extends Nanoc.Filter { | |
#classNames = {}; | |
#className = null; | |
#version = null; | |
#mdnBaseUrl = "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects"; | |
// ----- Helpers ----- | |
classSourceLink(data) { | |
this.sourceLink(data, "class") | |
}; | |
propertySourceLink(data) { | |
this.sourceLink(data, "property") | |
}; | |
sourceLink(item, type=null) { | |
`<a | |
class="document-source" | |
href="${item.srcUrl}" | |
${type ? ` title="View ${type} source"` : ""}> | |
${this.octicon("file-code")} | |
</a> | |
` | |
}; | |
markdown(text) { | |
text = this.linkReferences(text); | |
new Kramdown.Document(text, {input: "GFM", hardWrap: false}).toHtml | |
}; | |
linkReferences(text) { | |
let bracesLinkPattern = /\{(?<cname>\w+)?(::(?<fname>\w+))?\}/; | |
text.replaceAll(bracesLinkPattern, (match) => { | |
let link; | |
let cname = Regexp.lastMatch.namedCaptures.cname; | |
let fname = Regexp.lastMatch.namedCaptures.fname; | |
if (cname && this.#classNames[cname] && fname) { | |
return `[${cname}::${fname}](../${cname}/#instance-${fname})` | |
} else if (cname && fname) { | |
link = File.join(this.#mdnBaseUrl, cname, fname); | |
return `[${cname}::${fname}](${link})` | |
} else if (cname && this.#classNames[cname]) { | |
return `[${cname}](../${cname}/)` | |
} else if (cname) { | |
link = File.join(this.#mdnBaseUrl, cname); | |
return `[${cname}](${link})` | |
} else if (fname) { | |
return `[::${fname}](#instance-${fname})` | |
} else { | |
match.toString() | |
} | |
}) | |
}; | |
octicon(name) { | |
`<span class="octicon octicon-${name}"></span>` | |
}; | |
argument(arg) { | |
`<span class="argument">${arg.name}</span>` | |
}; | |
argumentList(func) { | |
let args = func.arguments; | |
`<span class="argument-list">(${args ? args.map(arg => this.argument(arg)).join(", ") : ""})</span>\n` | |
}; | |
method(func, scope) { | |
let id = `${scope}-${func.name}`; | |
let prefix = (() => { | |
switch (scope) { | |
case "instance": | |
return "::"; | |
case "class": | |
return "."; | |
default: | |
return "" | |
} | |
})(); | |
`<div | |
class="api-entry js-api-entry ${this.visibilityClass(func.visibility)}" | |
id="${id}"> | |
<h3 class="name"> | |
<a href="#${id}" class="js-api-name method-signature" name="${id}"> | |
${prefix}${func.name}${this.argumentList(func)} | |
</a> | |
${this.sourceLink(func)} | |
</h3> | |
<div class="method-summary-wrapper"> | |
${this.summary(func)} | |
${this.description(func)} | |
${func.arguments ? this.argumentsTable(func) : null} | |
${func.returnValues ? this.returnValuesTable(func) : null} | |
</div> | |
</div> | |
` | |
}; | |
returnValuesTable(func) { | |
`<table class="return-values"> | |
<thead> | |
<tr> | |
<th>Return values</th> | |
</tr> | |
</thead> | |
<tbody> | |
${this.returnValueRows(func.returnValues)} | |
</tbody> | |
</table> | |
` | |
}; | |
returnValueRows(retvals) { | |
retvals.map(retval => this.returnValueRow(retval)).join | |
}; | |
returnValueRow(retval) { | |
`<tr> | |
<td class="markdown-body"> | |
${this.markdown(retval.description)} | |
</td> | |
</tr> | |
` | |
}; | |
argumentsTable(func) { | |
`<table class="arguments"> | |
<thead> | |
<tr> | |
<th>Argument</th> | |
<th>Description</th> | |
</tr> | |
</thead> | |
<tbody> | |
${this.argumentRows(func.arguments)} | |
</tbody> | |
</table> | |
` | |
}; | |
argumentRows(args, level=0) { | |
args.map(arg => this.argumentRow(arg, level)).join | |
}; | |
argumentRow(arg, level) { | |
let children = arg.children; | |
`<tr class="markdown-body argument-depth-${level}"> | |
<td> | |
${this.markdown(`\`${arg.name}\``)} | |
</td> | |
<td> | |
${arg.isOptional ? "<span class=\"optional\">optional</span>" : ""} | |
${this.markdown(arg.description)} | |
</td> | |
</tr> | |
${children ? this.argumentRows( | |
children, | |
level + 1 | |
) : null} | |
` | |
}; | |
summary(obj) { | |
`<div class="summary markdown-body">\n ${this.markdown(obj.summary)}\n</div>\n` | |
}; | |
description(obj) { | |
let text = ((s) => { | |
s.slice(obj.summary); | |
return s | |
})(obj.description); | |
`<div class="body markdown-body"> | |
<div class="description"> | |
${this.markdown(text)} | |
</div> | |
</div> | |
` | |
}; | |
property(prop, scope) { | |
let id = `${scope}-${prop.name}`; | |
`<div | |
class="api-entry js-api-entry ${this.visibilityClass(prop.visibility)}" | |
id="${scope}-${prop.name}"> | |
<h3 class="name"> | |
<a href="#${id}" class="js-api-name" name="${id}"> | |
<span class="operator operator-instance">::</span>${prop.name.trim()} | |
</a> | |
${this.propertySourceLink(prop)} | |
</h3> | |
<div class="method-summary-wrapper"> | |
${this.summary(prop)} | |
</div> | |
</div> | |
` | |
}; | |
visibility(viz) { | |
switch (viz) { | |
case "Public": | |
"Essential"; | |
break; | |
default: | |
viz | |
} | |
}; | |
visibilityClass(viz) { | |
this.visibility(viz).toLowerCase | |
}; | |
visibilityLabel(data) { | |
`<span | |
class="label label-${this.visibilityClass(data.visibility)}" | |
title="This class is in the ${this.visibilityClass(data.visibility)} API"> | |
${this.visibility(data.visibility)} | |
</span> | |
` | |
}; | |
// ----- Page Sections ----- | |
classDescription(data) { | |
`<div class="markdown-body">\n ${this.markdown(data.description)}\n</div>\n` | |
}; | |
classExamples(data) { | |
let examples = Array.from(data.examples); | |
if (examples.length == 0) return ""; | |
let entries = examples.map((example) => { | |
let description; | |
this.description = ""; | |
if (example.description.length != 0) { | |
this.description = `<div class="description markdown-body">\n ${this.markdown(example.description)}\n</div>\n` | |
}; | |
return `<div class="example">\n ${description}\n ${this.markdown(example.raw)}\n</div>\n` | |
}).join("\n"); | |
`<h2 class="section">Examples</h2> | |
<div class="document-examples markdown-body"> | |
${entries} | |
</div> | |
` | |
}; | |
pageTitle(data) { | |
`<h2 class="page-title">\n ${data.name}\n ${this.visibilityLabel(data)}\n ${this.classSourceLink(data)}\n</h2>\n` | |
}; | |
uncategorizedMethods(data) { | |
let uncategorizedClassMethods = data.classMethods.filter(func => ( | |
func.sectionName == null | |
)); | |
let uncategorizedInstanceMethods = data.instanceMethods.filter(func => ( | |
func.sectionName == null | |
)); | |
if ((uncategorizedClassMethods + uncategorizedInstanceMethods).length == 0) { | |
return "" | |
}; | |
`<h2 class="detail-section">Methods</h2>\n${uncategorizedClassMethods.map(func => ( | |
this.method(func, "class") | |
)).join}\n${uncategorizedInstanceMethods.map(func => ( | |
this.method(func, "instance") | |
)).join}\n` | |
}; | |
instanceMethods(methods) { | |
let [extended, essential] = methods.partition(func => ( | |
func.visibility == "Extended" | |
)); | |
let html = ""; | |
html.push(essential.map(func => this.method(func, "instance")).join); | |
if (extended.any) { | |
if (essential.length == 0) { | |
html.push("<p class=\"no-methods-message\">This section only has Extended methods.</p>") | |
}; | |
html.push("<h4>Extended Methods</h4>"); | |
html.push(extended.map(func => this.method(func, "instance")).join) | |
}; | |
html | |
}; | |
sections(data) { | |
let sectionNames = data.sections.map(section => section.name).uniq; | |
let sectionText = sectionNames.map((sectionName) => { | |
let _classMethods = data.classMethods.filter(func => ( | |
func.sectionName == sectionName | |
)); | |
let _instanceProperties = data.instanceProperties.filter(prop => ( | |
prop.sectionName == sectionName | |
)); | |
let _instanceMethods = data.instanceMethods.filter(func => ( | |
func.sectionName == sectionName | |
)); | |
return `<h2 class="detail-section">${sectionName}</h2>\n${_classMethods.map(func => ( | |
this.method(func, "class") | |
)).join}\n${_instanceProperties.map(prop => this.property(prop, "instance")).join}\n${this.instanceMethods(_instanceMethods)}\n` | |
}); | |
sectionText.join("\n") | |
}; | |
run(content, params={}) { | |
this.#className = params.className; | |
this.#version = params.version; | |
let apiDir = `${Dir.pwd}/content/api/${this.#version}`; | |
Dir.glob( | |
`${apiDir}/*.json`, | |
entry => this.#classNames[File.basename(entry, ".json")] = true | |
); | |
let data = JSON.parse(content); | |
this.pageTitle(data) + this.classDescription(data) + this.classExamples(data) + this.uncategorizedMethods(data) + this.sections(data) | |
} | |
}; | |
ApiJsonFilter.identifier("apiJson") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment