Files
cell/scripts/modules/doc.js
2025-02-08 17:09:34 -06:00

287 lines
9.0 KiB
JavaScript

var ret = {}
var io = use('io')
// Helper: Given an object `obj`, return either:
// - If there's no prosperon.DOC, return ''.
// - If obj[prosperon.DOC] is a string, return that (unless a sub-property was requested).
// - If obj[prosperon.DOC] is an object, return its .doc field if no prop, or doc for that prop if provided.
var docOf = function(obj, prop) {
var block = obj[prosperon.DOC]
if (typeof block === 'string') {
if (prop) return ''
return block
}
if (!block || typeof block !== 'object') return ''
if (!prop) return block.doc || ''
return block[prop] || ''
}
// Reusable doc parsing logic for rewriting :param and :return.
function parseDocStr(docStr) {
var out = []
if (!docStr) return out
var docLines = docStr.split('\n')
var paramRe = /^:param\s+([A-Za-z0-9_]+)\s*:\s*(.*)$/
var returnRe = /^:return:\s*(.*)$/
for (var j = 0; j < docLines.length; j++) {
var line = docLines[j]
var pm = paramRe.exec(line)
var rm = returnRe.exec(line)
if (pm) {
out.push('**' + pm[1] + '**: ' + pm[2])
out.push('')
} else if (rm) {
out.push('**Returns**: ' + rm[1])
out.push('')
} else {
out.push(line)
}
}
return out
}
// Recursively write docs for an object and its sub-objects.
// level -> how many '#' in our markdown heading
// name -> the name of this object in the heading
function writeDocsForObject(obj, lines, level, name) {
var docStr = docOf(obj)
if (docStr) {
// Print heading for the object itself
var headingLine = Array(level + 1).join('#') + ' ' + name + '\n'
lines.push(headingLine)
var docOut = parseDocStr(docStr)
if (docOut.length > 0) lines.push(docOut.join('\n') + '\n')
}
// Enumerate properties
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) continue
var val = obj[prop]
var valDoc = val && (val[prosperon.DOC] || docOf(obj, prop)) || ''
// If it's a function, print a heading like "## area(r)"
if (typeof val === 'function') {
var paramMatches = []
var docLines = valDoc.split('\n')
var paramRe = /^:param\s+([A-Za-z0-9_]+)\s*:\s*(.*)$/
for (var j = 0; j < docLines.length; j++) {
var pm = paramRe.exec(docLines[j])
if (pm) paramMatches.push(pm[1])
}
var fnHeading = Array(level + 2).join('#') + ' ' + prop
// If paramMatches is not empty, list them in the heading
if (paramMatches.length > 0) fnHeading += '(' + paramMatches.join(', ') + ')\n'
else {
// fallback: parse from function signature
var m = val.toString().match(/\(([^)]*)\)/)
if (m) fnHeading += '(' + m[1].trim() + ')\n'
else fnHeading += '\n'
}
lines.push(fnHeading)
var parsed = parseDocStr(valDoc)
if (parsed.length > 0) lines.push(parsed.join('\n') + '\n')
}
// If it's a nested object, recursively write docs for it
else if (val && typeof val === 'object') {
// We only bother if the sub-object has any doc
// or if there's a good reason to drill deeper.
// If we want to always print sub-objects, remove the doc check.
var subObjDoc = docOf(val)
if (subObjDoc) {
writeDocsForObject(val, lines, level + 1, prop)
}
// If subObjDoc is empty but the object might contain sub-doc for
// its methods, you may want to call writeDocsForObject anyway.
else {
// We can still check if any child function has a doc:
var hasChildWithDoc = false
for (var sp in val) {
if (!val.hasOwnProperty(sp)) continue
var subVal = val[sp]
if (subVal && typeof subVal === 'function' && (subVal[prosperon.DOC] || docOf(val, sp))) {
hasChildWithDoc = true
break
}
}
if (hasChildWithDoc) writeDocsForObject(val, lines, level + 1, prop)
}
}
// else (if it's something else, e.g. number/string) you might skip it or do something special
}
}
var docs = io.enumerate("scripts/modules", 0)
docs = docs.filter(x => io.match("**/*.js", x)).map(x => x.name())
var APIPATH = '.src/docs/api/'
ret.print_api = function(obj) {
// Old console-based printing. We'll leave as-is or minimal changes if desired.
var topDoc = docOf(obj)
if (topDoc) console.log(' doc: ' + topDoc)
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) continue
var val = obj[prop]
console.log(prop)
var docStr = val[prosperon.DOC] || docOf(obj, prop)
if (docStr) console.log(' doc: ' + docStr)
if (typeof val === 'function') {
var m = val.toString().match(/\(([^)]*)\)/)
if (m) console.log(' function: ' + prop + '(' + m[1].trim() + ')')
}
}
}
ret.print_modules = function() {
for (var i = 0; i < docs.length; i++) {
var name = docs[i]
var mod = use(name)
console.log('MODULE: ' + name)
var modDoc = docOf(mod)
if (modDoc) console.log(' doc: ' + modDoc)
ret.print_api(mod)
console.log('')
}
}
// Writes each module's docs to .src/docs/api/<moduleName>.md
ret.write_modules = function() {
// rm all in APIPATH
var old = io.enumerate(APIPATH, 0)
old = old.filter(x => !io.match("**/index.md", x))
old = old.filter(x => !io.match("**/c_types", x))
old.forEach(x => io.rm(x))
if (!io.exists(APIPATH)) io.mkdir(APIPATH)
for (var i = 0; i < docs.length; i++) {
var name = docs[i]
var mod = use(name)
var lines = []
// Top-level heading for module
lines.push('# ' + name + '\n')
// Recursively write docs for the module at heading level 1
writeDocsForObject(mod, lines, 1, name)
var out = lines.join('\n')
io.slurpwrite(APIPATH + '/' + name + '.md', out)
}
}
ret.write_c_types = function() {
var CTYPEPATH = '.src/docs/api/c_types'
// Remove old ctype docs except index.md
var old = io.enumerate(CTYPEPATH, 0)
old = old.filter(x => !io.match("**/index.md", x))
old.forEach(x => io.rm(x))
if (!io.exists(CTYPEPATH)) io.mkdir(CTYPEPATH)
var cTypeNames = Object.keys(prosperon.c_types)
for (var i = 0; i < cTypeNames.length; i++) {
var name = cTypeNames[i]
var ctype = prosperon.c_types[name]
var lines = []
// Title
lines.push('# ' + name + '\n')
// If the ctype itself has a docstring (string or object.doc), include it
var ctypeDoc = docOf(ctype)
if (ctypeDoc) lines.push(ctypeDoc + '\n')
// Enumerate all own properties on the prototype (so we don't trigger getters)
var props = Object.getOwnPropertyNames(ctype)
for (var p = 0; p < props.length; p++) {
var propName = props[p]
if (propName === 'constructor') continue
var desc = Object.getOwnPropertyDescriptor(ctype, propName)
if (!desc) continue
if (typeof desc.value === 'function') writeMethod(lines, propName, desc.value, ctype)
if (typeof desc.get === 'function') writeGetter(lines, propName, desc.get, ctype)
if (typeof desc.set === 'function') writeSetter(lines, propName, desc.set, ctype)
}
var out = lines.join('\n')
io.slurpwrite(CTYPEPATH + '/' + name + '.md', out)
}
function writeMethod(lines, prop, fn, parent) {
var docStr = fn[prosperon.DOC] || docOf(parent, prop) || ''
var paramMatches = []
if (typeof fn === 'function') {
var docLines = docStr.split('\n')
var paramRe = /^:param\s+([A-Za-z0-9_]+)\s*:\s*(.*)$/
for (var j = 0; j < docLines.length; j++) {
var pm = paramRe.exec(docLines[j])
if (pm) paramMatches.push(pm[1])
}
}
var heading = '## ' + prop
if (paramMatches.length > 0) heading += '(' + paramMatches.join(', ') + ')\n'
else {
var m = fn.toString().match(/\(([^)]*)\)/)
if (m) heading += '(' + m[1].trim() + ')\n'
else heading += '\n'
}
lines.push(heading)
var docOut = parseDocStr(docStr)
if (docOut.length > 0) lines.push(docOut.join('\n') + '\n')
lines.push('')
}
function writeGetter(lines, prop, getterFn, parent) {
lines.push('## get ' + prop + '\n')
var docStr = getterFn[prosperon.DOC] || docOf(parent, prop) || ''
var docOut = parseDocStr(docStr)
if (docOut.length > 0) lines.push(docOut.join('\n') + '\n')
lines.push('')
}
function writeSetter(lines, prop, setterFn, parent) {
var docStr = setterFn[prosperon.DOC] || docOf(parent, prop) || ''
var paramMatches = []
if (typeof setterFn === 'function') {
var docLines = docStr.split('\n')
var paramRe = /^:param\s+([A-Za-z0-9_]+)\s*:\s*(.*)$/
for (var j = 0; j < docLines.length; j++) {
var pm = paramRe.exec(docLines[j])
if (pm) paramMatches.push(pm[1])
}
}
var heading = '## set ' + prop
if (paramMatches.length > 0) heading += '(' + paramMatches.join(', ') + ')\n'
else {
var m = setterFn.toString().match(/\(([^)]*)\)/)
if (m) heading += '(' + m[1].trim() + ')\n'
else heading += '\n'
}
lines.push(heading)
var docOut = parseDocStr(docStr)
if (docOut.length > 0) lines.push(docOut.join('\n') + '\n')
lines.push('')
}
}
return ret