diff --git a/admin/pages/DocsBrowser.svelte b/admin/pages/DocsBrowser.svelte
index 3ea813a..5f98139 100644
--- a/admin/pages/DocsBrowser.svelte
+++ b/admin/pages/DocsBrowser.svelte
@@ -77,6 +77,7 @@
}
export > heading h4 {
+ font-family: var(--font-family);
margin: 0px !important;
}
@@ -117,13 +118,17 @@
{#if docs_data}
{#each docs_data.exports as exp}
{#if exp.isa === "class"}
-
Class: { exp.name }
+
+
+ Class: { exp.name }
+
+
{#if exp.comment}
{@html exp.comment}
{/if}
-
+
{#each exp.methods as member}
@@ -131,19 +136,18 @@
{ member.name }
{docs_data.source}:{ member.line_start }
- { member.isa }
+ { member.isa } of { exp.name }
{#if member.static}static{/if}
{#if member.async}async{/if}
{#if member.generator}generator{/if}
- {#if exp.comment}
+ {#if member.comment}
- { @html exp.comment }
+ { @html member.comment }
{/if}
- Signature
@@ -166,7 +170,6 @@
{@html exp.comment}
{/if}
- Signature
diff --git a/commands/codedoc.js b/commands/codedoc.js
index 060b1d0..ba00f61 100644
--- a/commands/codedoc.js
+++ b/commands/codedoc.js
@@ -27,14 +27,21 @@ export const required = [
const RENDERER = create_renderer();
+/*
+ Strips 1 leading space from the comments, or the \s\* combinations
+ in traditional documentation comments.
+
+ If we strip more it'll mess up formatting in markdown for indentation
+ formats. Weirdly Remarkable seems to be able to handle leading spaces pretty
+ well so only need to remove one space or \s\* combinations like with
+ traditional comment docs.
+*/
const render_comment = (comment) => {
- // strip 1 leading space from the comments, if we strip more it'll
- // mess up formatting in markdown for indentation formats
- const lines = comment.split(/\n/).map(l => l.replace(/^\s/, ''));
+ const lines = comment.split(/\n/).map(l => l.replace(/^(\s*\*\s?|\s)/, ''));
return RENDERER.render(lines.join("\n"));
}
-// handy function for checking things are good and aborting
+/* Handy function for checking things are good and aborting. */
const check = (test, fail_message) => {
if(!test) {
log.error(fail_message);
@@ -61,8 +68,6 @@ class ParseWalker {
methods: [],
}
- new_class.comment = this.find_comment(new_class.line_start);
-
acorn_walk.simple(root, {
ClassDeclaration: (cls_node) => {
assert(cls_node.id.name === new_class.name, "Name of class changed!");
@@ -159,13 +164,13 @@ class ParseWalker {
}
/**
- * Find the nearest comment to this line, giving
- * about 2 lines of slack.
+ Find the nearest comment to this line, giving
+ about 2 lines of slack.
*/
find_comment(line) {
for(let c of this.comments) {
const distance = c.end - line;
- if(!c.found && distance < 0 && distance > -3) {
+ if(!c.found && distance == -1) {
c.found = true;
return render_comment(c.value);
}
diff --git a/lib/docgen.js b/lib/docgen.js
index 1660194..f781a25 100644
--- a/lib/docgen.js
+++ b/lib/docgen.js
@@ -97,7 +97,7 @@ export const create_renderer = (toc) => {
let level = tokens[idx].hLevel;
let content = tokens[idx + 1].content;
let slug = slugify(content, {lower: true, strict: true});
- toc.push({level, content, slug});
+ if(toc) toc.push({level, content, slug});
return ``;
}
diff --git a/lib/ormish.js b/lib/ormish.js
index d4ce3dd..3541c58 100644
--- a/lib/ormish.js
+++ b/lib/ormish.js
@@ -3,10 +3,18 @@ import knexConfig from 'knex';
import assert from 'assert';
import { attachPaginate } from 'knex-paginate';
+/*
+ A preconfigured knex.js driver using the config.development configuration
+ by default.
+
+ _TODO_: Need to make this configurable, even though I just use one config right now since I run sqlite3 all the time.
+ */
export const knex = knexConfig(config.development);
-// run the PERF_TRICKS to configure sqlite3 when thing start, really need to make this
-// a configuration and only do it with sqlite3, but for now just get this done
+/*
+ run the PERF_TRICKS to configure sqlite3 when thing start, really need to make this
+ a configuration and only do it with sqlite3, but for now just get this done
+ */
if(config.development.client === "sqlite3") {
const PERF_TRICKS = [
"pragma journal_mode = WAL", // use a WAL journal to not block writers/readers
@@ -25,6 +33,10 @@ if(config.development.client === "sqlite3") {
attachPaginate();
+/*
+ Filled in by `load_schema` to give access to the database scheme in the admin
+ tool and generally through the API.
+ */
export const SCHEMA = {};
const load_schema = async () => {
@@ -43,11 +55,11 @@ await load_schema();
is called by Model.validation and you can call it directly to get rules
for a database table.
- + `name string` - the table name.
- + `rules Object` - default rules with empty "" for the rules you want filled in
- + `all boolean` - set this to true if you want everything
- + `no_id boolean` - defaults to true, set false if you also want the id
- + `return Object` - the resulting rules to use with Validator
+ 1. `name string` - the table name.
+ 2. `rules Object` - default rules with empty "" for the rules you want filled in
+ 3. `all boolean` - set this to true if you want everything
+ 4. `no_id boolean` - defaults to true, set false if you also want the id
+ 5. `return Object` - the resulting rules to use with Validator
*/
export const validation = (name, rules, all=false, no_id=true) => {
assert(rules, "rules parameter is required and will be modified");
@@ -99,20 +111,73 @@ export const validation = (name, rules, all=false, no_id=true) => {
return rules;
}
+/*
+ The base class for all models found in `lib/models.js`. You use this by extending it with:
+
+ ```javascript
+ class User extends Model.from_table('user') {
+ }
+ ```
+
+ This will create a `User` class that is automatically configured using the SCHEMA create from the `user` table in your database. You won't need to define the attributes on this class as it will be correctly populated from the database.
+
+ The database is therefore the "source of truth" for all of the models. You can then add functions to extend what this class does.
+ */
export class Model {
+
+ /*
+ Allows you to build a new object of this Model with the given `attr`
+ already set, but really you should use the `Model.from(attr)` method instead.
+ This does _no_ object sanitization with `Model.clean(attr)` method, and if
+ it doesn't match the underlying database it will throw an exception.
+
+ - `attr Object` - the attributes for this model
+ */
constructor(attr) {
assert(attr, "Must give attributes.");
Object.assign(this, attr);
}
+ /*
+ How to actually create a new instance of this model. This
+ will do two things:
+
+ 1. Correctly use the schema for the subclass model.
+ 2. Sanitize the input to remove anything that shouldn't be in the database.
+
+ The `also_remove` parameter is a list of additional keys to also scrub from the object.
+
+ - `attr Object` -- The attributes this should start with.
+ - `also_remove Array` -- list of additional attributes to remove.
+ */
static from(attr, also_remove=undefined) {
return new this(this.clean(attr, also_remove));
}
+ /*
+ Returns an object representing the schema for this Model. Remember that this
+ will reflect what's in the database schema, which is formatted however
+ `knex.js` formats your database Schema. Might not be portable between
+ databases and only tested with SQlite3.
+
+ _This is an attribute accessor, so just do `obj.schema` rather than call it like a function._
+
+ - `return Object` - The schema for this model.
+ */
get schema() {
return this.constructor.schema;
}
+ /*
+ Uses the `this.schema` scrub out any attributes that are not valid for the
+ schema. This is effectively a whitelist for the allowed attributes based on
+ the database schema. You can use the `also_remove` parameter to list
+ additional attributes to remove, which you should do to sanitize incoming
+ objects for things like password fields.
+
+ - `attr Object` - The attributes to clean.
+ - `also_remove Array` - Additional attributes to remove.
+ */
static clean(attr, also_remove=undefined) {
assert(attr, "Must give attributes to clean.");
@@ -128,18 +193,18 @@ export class Model {
return this.constructor.table_name;
}
- /**
- * Returns an object of basic rules meant for lib/api.js:validate
- * based on what's in the database. It's meant to be an easy to
- * pass in starter which you can augment. It expects a set of rules
- * with keys you want configured. Any key that's set to an empty string ""
- * will be filled in with a minimum rule to match the database schema.
- *
- * It's designed to be called once at the top of an api/ handler to get
- * a basic set of rules. You could also run it to print out the rules then
- * simply write the rules directly where you need them.
- *
- * @param rules {Object} - rules specifier
+ /*
+ Returns an object of basic rules meant for lib/api.js:validate
+ based on what's in the database. It's meant to be an easy to
+ pass in starter which you can augment. It expects a set of rules
+ with keys you want configured. Any key that's set to an empty string ""
+ will be filled in with a minimum rule to match the database schema.
+
+ It's designed to be called once at the top of an api/ handler to get
+ a basic set of rules. You could also run it to print out the rules then
+ simply write the rules directly where you need them.
+
+ - `param rules {Object}` - rules specifier
*/
static validation(rules) {
return validation(this.table_name, rules);
@@ -204,13 +269,13 @@ export class Model {
return new this(attr);
}
- /**
- * Implements an upsert (insert but update on conflict) for Postgres, MySQL, and SQLite3 only.
- *
- * @param { Object } attr - The attributes to insert or update.
- * @param { string } conflict_key - The key that can cause a conflict then update.
- * @param { boolean } merge - Defaults to true and will change the record. false will ignore and not update on conflict.
- * @return { number } - id or undefined
+ /*
+ Implements an upsert (insert but update on conflict) for Postgres, MySQL, and SQLite3 only.
+
+ + attr { Object } - The attributes to insert or update.
+ + conflict_key { string } - The key that can cause a conflict then update.
+ + merge { boolean } - Defaults to true and will change the record. false will ignore and not update on conflict.
+ + return { number } - id or undefined
*/
static async upsert(attr, conflict_key, merge=true) {
assert(conflict_key !== undefined, `You forgot to set the conflict_key on upsert to table ${this.table_name}`);
diff --git a/package.json b/package.json
index 799ce88..d924879 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
},
"scripts": {
"dev": "run-p -l -n api queue watch tracker rendered-watch",
- "DANGER_ADMIN": "cross-env DANGER_ADMIN=1 run-p -l -n api queue tracker watch rendered-watch",
+ "DANGER_ADMIN": "cross-env DANGER_ADMIN=1 run-p -l -n api queue tracker watch rendered-watch docs",
"prod": "run-p api queue",
"tracker": "node ./bando.js tracker",
"build": "node ./bando.js build --config build.prod.json",
@@ -17,6 +17,7 @@
"api": "nodemon ./bando.js api",
"queue": "nodemon ./bando.js queue",
"test": "npx ava tests/**/*.js",
+ "docs": "nodemon bando.js codedoc --output public/docs/api lib/*.js client/*.js",
"modules": "./bando.js load",
"modules-watch": "nodemon --watch ../ljsthw-private/db/modules/ --ext .md,.js,.sh ./bando.js load",
"rendered-watch": "nodemon --ignore \"rendered/build/**/*\" --watch ./rendered --watch static -e md,svelte,js,css ./bando.js rendered",