From 28cfe559210c46a2e2b7104291869dd9d111e516 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 16 Dec 2022 03:45:12 -0500 Subject: [PATCH] Code docs output is serviceable and there's a UI now. --- admin/pages/DocsBrowser.svelte | 89 ++++++++++++++++++++++++++++++ admin/routes.js | 2 + commands/codedoc.js | 98 ++++++++++++++++++++-------------- 3 files changed, 150 insertions(+), 39 deletions(-) create mode 100644 admin/pages/DocsBrowser.svelte diff --git a/admin/pages/DocsBrowser.svelte b/admin/pages/DocsBrowser.svelte new file mode 100644 index 0000000..9fc1c8c --- /dev/null +++ b/admin/pages/DocsBrowser.svelte @@ -0,0 +1,89 @@ + + + + + + + + + +

Docs

+ + {#each Object.keys(index) as item} + {item} + {/each} + +
+ + + {#if docs_data} + {#each docs_data.exports as exp} +

{ url }

+

{exp.name}

+ {#if exp.comment} +
+{ exp.comment.value }
+              
+ {/if} + {#if exp.isa === "class"} + {#each exp.methods as method} +

{ method.name }

+ {#if exp.comment} +
+{ exp.comment.value }
+                  
+ {/if} + + {/each} + {:else} + + {/if} + {/each} + {/if} +
+
+
+
+ diff --git a/admin/routes.js b/admin/routes.js index 55db28c..0d7b6e4 100644 --- a/admin/routes.js +++ b/admin/routes.js @@ -8,8 +8,10 @@ import ReadUpdate from "$/admin/pages/ReadUpdate.svelte"; import EmailConfig from "$/admin/pages/EmailConfig.svelte"; import NotFound from "$/client/pages/NotFound.svelte"; import Components from "./bando/Components.svelte"; +import DocsBrowser from "$/admin/pages/DocsBrowser.svelte"; export default { + "/docs/*": DocsBrowser, "/bando/components/:name?": Components, "/errors/": Errors, "/routes/": Routes, diff --git a/commands/codedoc.js b/commands/codedoc.js index 7d5a869..447bbfe 100644 --- a/commands/codedoc.js +++ b/commands/codedoc.js @@ -2,8 +2,7 @@ import fs from "fs"; import assert from "assert"; import logging from '../lib/logging.js'; -import { mkdir, mkdir_to } from "../lib/builderator.js"; -import glob from "fast-glob"; +import { mkdir, mkdir_to, glob } from "../lib/builderator.js"; import path from "path"; import template from "lodash/template.js"; import * as acorn from "acorn"; @@ -18,7 +17,7 @@ export const options = [ ] // example of a positional argument, it's the 1st argument to main -export const argument = ["source", "source directory"]; +export const argument = ["", "source input globs"]; // put required options in the required variable export const required = [ @@ -38,9 +37,10 @@ const dump = (obj) => { } class ParseWalker { - constructor(comments) { + constructor(comments, code) { this.comments = comments; this.exported = []; + this.code = code; } handle_class(root) { @@ -56,7 +56,8 @@ class ParseWalker { acorn_walk.simple(root, { ClassDeclaration: (cls_node) => { assert(cls_node.id.name === new_class.name, "Name of class changed!"); - this.exported.push(new_class); + new_class.range = [cls_node.start, cls_node.body.start]; + this.add_export(cls_node.id, new_class); }, MethodDefinition: (meth_node) => { @@ -67,9 +68,13 @@ class ParseWalker { generator: meth_node.value.generator, name: meth_node.key.name, line_start: meth_node.loc.start.line, + range: [meth_node.start, meth_node.value.body.start], params: this.handle_params(meth_node.value.params), - comment: this.find_comment(meth_node.loc.start.line) + comment: this.find_comment(meth_node.loc.start.line), } + + new_method.code = this.slice_code(new_method.range); + new_class.methods.push(new_method); } }); @@ -106,51 +111,39 @@ class ParseWalker { exp.name = id.name; exp.line_start = id.loc.start.line; exp.comment = this.find_comment(exp.line_start); + exp.code = this.slice_code(exp.range); this.exported.push(exp); } + slice_code(range) { + return this.code.slice(range[0], range[1]); + } + handle_arrow_func(id, arrow) { this.add_export(id, { isa: "function", async: arrow.async, generator: arrow.generator, expression: arrow.expression, + range: [id.start, arrow.body.start], params: this.handle_params(arrow.params), }); } handle_variable(root) { const declare = root.declaration.declarations[0]; - // console.log("VARIABLE", declare); const id = declare.id; + const _node = declare.init; + const init_is = declare.init.type; - if(declare.init.type === "ArrowFunctionExpression") { + if(init_is === "ArrowFunctionExpression") { this.handle_arrow_func(id, declare.init); } else { - acorn_walk.simple(root, { - Literal: (_node) => { - this.add_export(id, { - isa: _node.type.toLowerCase(), - value: _node.value, - raw: _node.raw - }); - }, - CallExpression: (_node) => { - this.add_export(id, { - isa: _node.type.toLowerCase(), - identifier: _node.callee.name, - arguments: this.handle_params(_node.arguments) - }); - }, - MemberExpression: (_node) => { - this.add_export(id, { - isa: _node.type.toLowerCase(), - object: _node.object.name, - property: _node.property.name, - computed: _node.computed, - options: _node.optional, - }); - } + this.add_export(id, { + isa: _node.type.toLowerCase(), + value: _node.value, + range: declare.range, + raw: _node.raw }); } } @@ -186,17 +179,16 @@ class ParseWalker { } } -export const main = async (arg, opts) => { - const code = fs.readFileSync(arg); +const parse_source = (source) => { + const code = fs.readFileSync(source); - mkdir(opts.output); let comments = []; const acorn_opts = { sourceType: "module", ecmaVersion: "2023", locations: true, - sourceFile: arg, + sourceFile: source, ranges: true, onComment: comments } @@ -213,17 +205,45 @@ export const main = async (arg, opts) => { } }); - const walker = new ParseWalker(comments); + const walker = new ParseWalker(comments, code.toString()); // acorn is stupid and they grab a reference to the functions so that _removes_ // this from the object, instead of just...calling walker.function() like a normal person acorn_walk.simple(parsed, { ExportNamedDeclaration: (_node) => walker.handle_export(_node), }); - console.log(dump({ + return { + // normalize to / even on windows + source: source.replaceAll("\\", "/"), exports: walker.exported, orphan_comments: walker.comments.filter(c => !c.found) - })); + }; +} + +export const main = async (source_globs, opts) => { + const index = {}; + mkdir_to(opts.output); + + for(let source of source_globs) { + const source_list = glob(source); + + for(let fname of source_list) { + const result = parse_source(fname); + + const target = `${path.join(opts.output, fname)}.json`; + mkdir_to(target); + fs.writeFileSync(target, dump(result)); + + const name = fname.replaceAll("\\", "/").slice(2); + + index[name] = result.exports.map(e => { + return {isa: e.isa, name: e.name}; + }); + } + } + // now write the grand index + const index_name = path.join(opts.output, "index.json"); + fs.writeFileSync(index_name, dump(index)); process.exit(0); }