diff --git a/README.md b/README.md index f16b4d7..258db1b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -16. Then continue studying anything you find interesting and trying to change the existing pages. - -This should give you a quick crash course in how the framework operates while I work on better documentation and instructions.# bandolier-template +# Introduction -The Bandolier (aka `bando`) is an educational web framework featured in the Learn JavaScript the Hard Way course. The Bandolier contains all of the features a full stack developer would need to learn, but with smaller easier to understand implementations that are fully visible in the project. It includes implementations of: +The Bandolier (aka `bando`) is an educational web framework featured in the [Learn JavaScript the Hard Way](https://learnjsthehardway.com) course. The Bandolier contains all of the features a full stack developer would need to learn, but with smaller easier to understand implementations that are fully visible in the project. It includes implementations of: * JSON APIs backends. * Multi-page and Single-page web UIs. @@ -17,7 +15,8 @@ The Bandolier (aka `bando`) is an educational web framework featured in the Lear * Helpful video processing in JavaScript. * Template generators to get started with code. * Full but simple database administrator in the browser. -* Basic Discord integration. +* Basic [Discord](https://discord.com) integration. +* Custom documentation generation for APIs using [acorn](https://github.com/acornjs/acorn). * Includes common things you need like [lucide](lucide.dev) icons, Email DNS testing, web log analysis, in an easy to extend administrator tool. * All implemented in reasonably sized pieces of code you can study on your own. @@ -31,7 +30,7 @@ This is the template project that's checked out and configured when you run the First, install the [ljsthw-bandolier](https://git.learnjsthehardway.com/learn-javascript-the-hard-way/ljsthw-bandolier#ljsthw-bandolier): -``` +```shell npm install git+https://git.learnjsthehardway.com/learn-javascript-the-hard-way/ljsthw-bandolier.git ``` @@ -39,7 +38,7 @@ That will create a command you'll use to manage installs and updates of the [Ban You should now be able to do this: -``` +```shell npx bando-up --version npx bando-up --help ``` @@ -48,7 +47,7 @@ If you can't then refer to the documentation for `ljsthw-bandolier`, _especially If you can run the `npx bando-up` command then use it to create your first project: -``` +```shell npx bando-up create my-project ``` @@ -58,7 +57,7 @@ This will checkout the [Bandolier Template](https://git.learnjsthehardway.com/le If you checked out your first project into `my-project` then do this: -``` +```shell cd my-project npm install ``` @@ -67,13 +66,13 @@ That will move you into the project directory and install all of the required so Once `npm install` finishes you can configure the application: -``` +```shell npm run knex migrate:latest ``` This will setup your database, and then you can finally do the initialize command to finish the setup: -``` +```shell node bando.js init ``` @@ -81,7 +80,7 @@ This will configure some items, rerun the migrations just in case, and copy any Open another Terminal window and start the app in `DANGER_ADMIN` mode: -``` +```shell npm run DANGER_ADMIN ``` @@ -97,7 +96,7 @@ The final test is switch back to the browser and refresh the page to see that yo In theory you shouldn't need playwright to download browsers, so just tell it not to. -``` +```shell PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install ``` @@ -107,7 +106,7 @@ This isn't as needed on Linux as OSX, because the Playwright project forces peop If this doesn't work then this might https://github.com/nodejs/node-gyp/blob/main/docs/Updating-npm-bundled-node-gyp.md which says that npm updates don't update the node-gyp it uses internally. Depending on your version you will have to run different commands, but this works for 7,8, and 9 versions: -``` +```shell npm explore npm/node_modules/@npmcli/run-script -g -- npm_config_global=false npm install node-gyp@latest ``` diff --git a/admin/pages/DocsBrowser.svelte b/admin/pages/DocsBrowser.svelte index 87c1802..b47041e 100644 --- a/admin/pages/DocsBrowser.svelte +++ b/admin/pages/DocsBrowser.svelte @@ -9,14 +9,15 @@ let index = {}; let docs_data; let url; + let readme_docs; export let params; const caps_to_icon = { - "BUG": "bug", - "TODO": "clipboard-check", - "WARNING": "alert-triangle", - "FOOTGUN": "bomb", - "DEPRECATED": "axe" + "_BUG_": "bug", + "_TODO_": "clipboard-check", + "_WARNING_": "alert-triangle", + "_FOOTGUN_": "bomb", + "_DEPRECATED_": "axe" } const jump = (id) => { @@ -29,11 +30,14 @@ } const load_docs = async (to_load) => { - const [status, data] = await api.get(`/docs/api/${to_load}.json`); - - docs_data = status === 200 ? data : {}; - url = to_load; - jump_top(); + // only load if the URL changed + if(to_load !== url) { + const [status, data] = await api.get(`/docs/api/${to_load}.json`); + + docs_data = status === 200 ? data : {}; + url = to_load; + jump_top(); + } } const load_index = async () => { @@ -42,6 +46,22 @@ index = status === 200 ? data : {}; } + const load_readme = async () => { + if(!readme_docs) { + console.log("LOAD README"); + const [status, data] = await api.blob("/docs/api/index.html"); + + if(status === 200) { + readme_docs = await data.text(); + } else { + readme_docs = `ERROR LOADING README: ${status}` + } + } + + url = "/docs/"; + console.log("URL", url, "README", readme_docs !== undefined, "PARAMS", params); + } + const type_to_syntax = { "callexpression": "()", "objectexpression": "{}", @@ -51,11 +71,16 @@ } onMount(async () => { + await load_readme(); await load_index(); }); - $: if(params.wild && params.wild !== url) { - load_docs(params.wild); + $: if(params.wild) { + load_docs(params.wild) + url = url; // the most irritating thing about Svelte + } else { + load_readme(); + url = url; } @@ -85,6 +110,10 @@ max-width: calc(100vw - 300px); } + right > div { + padding: 0.5rem; + } + export { border-top: 1px solid var(--value5); padding: 0.5rem; @@ -175,8 +204,9 @@ -

Docs

- +

API Docs

+ + README {#each Object.keys(index) as item} {item} {/each} @@ -184,7 +214,13 @@
- {#if docs_data} + {#if url === "/docs/"} +
+ {#if readme_docs} + {@html readme_docs} + {/if} +
+ {:else if docs_data} {#each docs_data.exports as exp} jump(exp.slug) }>{ exp.name } @@ -202,11 +238,10 @@ {/each} -

{ url }

- {#if docs_data.comment} - {@html docs_data.comment} - {/if} - +

{ url }

+ {#if docs_data.comment} + {@html docs_data.comment} + {/if}
{#each docs_data.exports as exp} diff --git a/client/api.js b/client/api.js index e0c002c..2746782 100644 --- a/client/api.js +++ b/client/api.js @@ -330,6 +330,29 @@ export const raw = async (url, method, body, unauthed_action) => { } } +/* + Used for fetching basic resources, not JSON data from the API. Uses the + same return signature as `raw()` but doesn't attempt a JSON conversion, + and only does a "GET" request. It returns the result of `res.blob()` + so you can convert it to a string or JSON as you need, or leave it if + it's a binary file. + + + `url string` -- URL to get. + + ___return___ `Array[status, Blob|{}]` -- Returns a Blob or when status is 500 a `{"message": "Error message"}`. + */ +export const blob = async (url) => { + let data; + + try { + let res = await fetch(url, fetch_opts); + data = await res.blob(); + return [res.status, data]; + } catch(error) { + log.error(error, "Failed to parse reply body as JSON. Text is:", data, "error", error, "URL", url); + return [500, {"message": "Exception processing request. See log.debug."}]; + } +} + /* The GET method request. To keep things consistent with the other requests this accepts a `data` parameter, but it URL @@ -439,5 +462,5 @@ export const schema = async (table) => { export default { post, get, put, del, mock, options, fetch_opts, - logout_user, validate, can_submit, clean_form, schema + logout_user, validate, can_submit, clean_form, schema, blob } diff --git a/commands/codedoc.js b/commands/codedoc.js index 2754026..0b9601b 100644 --- a/commands/codedoc.js +++ b/commands/codedoc.js @@ -15,7 +15,8 @@ export const description = "Describe your command here." // your command uses the npm package commander's options format export const options = [ - ["--quiet", "Don't output the stats at the end."] + ["--quiet", "Don't output the stats at the end."], + ["--readme", "README file to use as the initial page", "README.md"] ] // example of a positional argument, it's the 1st argument to main @@ -329,6 +330,10 @@ export const main = async (source_globs, opts) => { const index_name = path.join(opts.output, "index.json"); fs.writeFileSync(index_name, dump(index)); + // render the README.md to the initial docs + const readme_name = path.join(opts.output, "index.html"); + const md_out = RENDERER.render(fs.readFileSync(opts.readme).toString()); + fs.writeFileSync(readme_name, md_out); const percent = Math.floor(100 * STATS.docs / STATS.total); diff --git a/lib/builderator.js b/lib/builderator.js index 7b6bcff..edffb92 100644 --- a/lib/builderator.js +++ b/lib/builderator.js @@ -11,7 +11,7 @@ import Path from "path"; import { execSync } from "child_process"; /* - Fixes some common problems with `fast-glob` on windows. I removes + Fixes some common problems with `fast-glob` on windows. It removes leading "C:\" from paths and replaces all of the `\\` with `/`. __BUG__: This obviously won't work if you're on a different drive than C:.