Add the ability to specify a README and make that the initial first page for the docs browser.

main
Zed A. Shaw 2 years ago
parent 14b7337949
commit 323ae3dc2a
  1. 27
      README.md
  2. 55
      admin/pages/DocsBrowser.svelte
  3. 25
      client/api.js
  4. 7
      commands/codedoc.js
  5. 2
      lib/builderator.js

@ -1,8 +1,6 @@
16. Then continue studying anything you find interesting and trying to change the existing pages. # Introduction
This should give you a quick crash course in how the framework operates while I work on better documentation and instructions.# bandolier-template
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. * JSON APIs backends.
* Multi-page and Single-page web UIs. * 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. * Helpful video processing in JavaScript.
* Template generators to get started with code. * Template generators to get started with code.
* Full but simple database administrator in the browser. * 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. * 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. * 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): 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 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: You should now be able to do this:
``` ```shell
npx bando-up --version npx bando-up --version
npx bando-up --help 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: If you can run the `npx bando-up` command then use it to create your first project:
``` ```shell
npx bando-up create my-project 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: If you checked out your first project into `my-project` then do this:
``` ```shell
cd my-project cd my-project
npm install 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: Once `npm install` finishes you can configure the application:
``` ```shell
npm run knex migrate:latest npm run knex migrate:latest
``` ```
This will setup your database, and then you can finally do the initialize command to finish the setup: This will setup your database, and then you can finally do the initialize command to finish the setup:
``` ```shell
node bando.js init 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: Open another Terminal window and start the app in `DANGER_ADMIN` mode:
``` ```shell
npm run DANGER_ADMIN 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. In theory you shouldn't need playwright to download browsers, so just tell it not to.
``` ```shell
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install 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: 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 npm explore npm/node_modules/@npmcli/run-script -g -- npm_config_global=false npm install node-gyp@latest
``` ```

@ -9,14 +9,15 @@
let index = {}; let index = {};
let docs_data; let docs_data;
let url; let url;
let readme_docs;
export let params; export let params;
const caps_to_icon = { const caps_to_icon = {
"BUG": "bug", "_BUG_": "bug",
"TODO": "clipboard-check", "_TODO_": "clipboard-check",
"WARNING": "alert-triangle", "_WARNING_": "alert-triangle",
"FOOTGUN": "bomb", "_FOOTGUN_": "bomb",
"DEPRECATED": "axe" "_DEPRECATED_": "axe"
} }
const jump = (id) => { const jump = (id) => {
@ -29,12 +30,15 @@
} }
const load_docs = async (to_load) => { const load_docs = async (to_load) => {
// only load if the URL changed
if(to_load !== url) {
const [status, data] = await api.get(`/docs/api/${to_load}.json`); const [status, data] = await api.get(`/docs/api/${to_load}.json`);
docs_data = status === 200 ? data : {}; docs_data = status === 200 ? data : {};
url = to_load; url = to_load;
jump_top(); jump_top();
} }
}
const load_index = async () => { const load_index = async () => {
const [status, data] = await api.get("/docs/api/index.json"); const [status, data] = await api.get("/docs/api/index.json");
@ -42,6 +46,22 @@
index = status === 200 ? data : {}; 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 = `<callout class="error">ERROR LOADING README: ${status}</callout>`
}
}
url = "/docs/";
console.log("URL", url, "README", readme_docs !== undefined, "PARAMS", params);
}
const type_to_syntax = { const type_to_syntax = {
"callexpression": "()", "callexpression": "()",
"objectexpression": "{}", "objectexpression": "{}",
@ -51,11 +71,16 @@
} }
onMount(async () => { onMount(async () => {
await load_readme();
await load_index(); await load_index();
}); });
$: if(params.wild && params.wild !== url) { $: if(params.wild) {
load_docs(params.wild); load_docs(params.wild)
url = url; // the most irritating thing about Svelte
} else {
load_readme();
url = url;
} }
</script> </script>
@ -85,6 +110,10 @@
max-width: calc(100vw - 300px); max-width: calc(100vw - 300px);
} }
right > div {
padding: 0.5rem;
}
export { export {
border-top: 1px solid var(--value5); border-top: 1px solid var(--value5);
padding: 0.5rem; padding: 0.5rem;
@ -175,8 +204,9 @@
<Layout footer={ false} header={ false } centered={ true } fullscreen={ true }> <Layout footer={ false} header={ false } centered={ true } fullscreen={ true }>
<sidebar> <sidebar>
<top><h4><a href="/" use:link><Icon name="chevrons-up" /> Docs</a></h4></top> <top><h4><a href="/" use:link><Icon name="chevrons-up" />API Docs</a></h4></top>
<items> <items>
<a class:active={ url === "/docs/" } href="/docs/" use:link>README</a>
{#each Object.keys(index) as item} {#each Object.keys(index) as item}
<a class:active={ item === url } href="/docs/{item}" use:link>{item}</a> <a class:active={ item === url } href="/docs/{item}" use:link>{item}</a>
{/each} {/each}
@ -184,7 +214,13 @@
</sidebar> </sidebar>
<right> <right>
{#if docs_data} {#if url === "/docs/"}
<div id="top-scroll">
{#if readme_docs}
{@html readme_docs}
{/if}
</div>
{:else if docs_data}
<toc id="top-scroll"> <toc id="top-scroll">
{#each docs_data.exports as exp} {#each docs_data.exports as exp}
<span class:no-doc={ !exp.comment } on:click={ () => jump(exp.slug) }>{ exp.name } <span class:no-doc={ !exp.comment } on:click={ () => jump(exp.slug) }>{ exp.name }
@ -206,7 +242,6 @@
{#if docs_data.comment} {#if docs_data.comment}
{@html docs_data.comment} {@html docs_data.comment}
{/if} {/if}
</module-header> </module-header>
{#each docs_data.exports as exp} {#each docs_data.exports as exp}

@ -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 The GET method request. To keep things consistent with the
other requests this accepts a `data` parameter, but it URL other requests this accepts a `data` parameter, but it URL
@ -439,5 +462,5 @@ export const schema = async (table) => {
export default { export default {
post, get, put, del, mock, options, fetch_opts, 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
} }

@ -15,7 +15,8 @@ export const description = "Describe your command here."
// your command uses the npm package commander's options format // your command uses the npm package commander's options format
export const options = [ 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 // 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"); const index_name = path.join(opts.output, "index.json");
fs.writeFileSync(index_name, dump(index)); 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); const percent = Math.floor(100 * STATS.docs / STATS.total);

@ -11,7 +11,7 @@ import Path from "path";
import { execSync } from "child_process"; 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 `/`. 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:. __BUG__: This obviously won't work if you're on a different drive than C:.

Loading…
Cancel
Save