diff --git a/framework/backend/entry-passthru.ts b/framework/backend/entry-passthru.ts
new file mode 100644
index 0000000..1273b67
--- /dev/null
+++ b/framework/backend/entry-passthru.ts
@@ -0,0 +1,4 @@
+import "@paperclover/console/inject";
+export default app;
+
+import app from "#backend";
diff --git a/framework/css.ts b/framework/css.ts
index c7063c5..bc7ba8f 100644
--- a/framework/css.ts
+++ b/framework/css.ts
@@ -64,7 +64,7 @@ export async function bundleCssFiles(
}),
);
},
- } satisfies Plugin;
+ } satisfies esbuild.Plugin;
const build = await esbuild.build({
bundle: true,
entryPoints: ["$input$"],
@@ -85,6 +85,5 @@ export async function bundleCssFiles(
return outputFiles[0].text;
}
-import type { Plugin } from "esbuild";
import * as esbuild from "esbuild";
-import * as fs from "./fs.ts";
+import * as fs from "#sitegen/fs";
diff --git a/framework/generate.tsx b/framework/generate.tsx
index cbe24f7..69825f2 100644
--- a/framework/generate.tsx
+++ b/framework/generate.tsx
@@ -290,10 +290,10 @@ import { OnceMap, Queue } from "./queue.ts";
import { Incremental } from "./incremental.ts";
import * as bundle from "./bundle.ts";
import * as css from "./css.ts";
-import * as fs from "./fs.ts";
-import { Spinner, withSpinner } from "@paperclover/console/Spinner";
-import * as meta from "./meta.ts";
import * as ssr from "./engine/ssr.ts";
-import * as sg from "#sitegen";
import * as hot from "./hot.ts";
+import * as fs from "#sitegen/fs";
+import * as sg from "#sitegen";
import * as path from "node:path";
+import * as meta from "#sitegen/meta";
+import { Spinner, withSpinner } from "@paperclover/console/Spinner";
diff --git a/framework/hot.ts b/framework/hot.ts
index 8e60aac..1f612c4 100644
--- a/framework/hot.ts
+++ b/framework/hot.ts
@@ -236,7 +236,7 @@ declare module "node:module" {
): unknown;
}
-import * as fs from "./fs.ts";
+import * as fs from "./lib/fs.ts";
import * as path from "node:path";
import { pathToFileURL } from "node:url";
import * as esbuild from "esbuild";
diff --git a/framework/incremental.ts b/framework/incremental.ts
index 793cf07..d77f676 100644
--- a/framework/incremental.ts
+++ b/framework/incremental.ts
@@ -252,11 +252,10 @@ export interface SerializedMeta {
script: [key: string, value: string][];
}
-import * as path from "node:path";
-import * as fs from "./fs.ts";
+import * as fs from "#sitegen/fs";
import * as zlib from "node:zlib";
import * as util from "node:util";
import { Queue } from "./queue.ts";
import * as hot from "./hot.ts";
-import * as mime from "./mime.ts";
+import * as mime from "#sitegen/mime";
import { Buffer } from "node:buffer";
diff --git a/framework/lib/assets.ts b/framework/lib/assets.ts
index 9dc295b..751571f 100644
--- a/framework/lib/assets.ts
+++ b/framework/lib/assets.ts
@@ -105,7 +105,7 @@ function assetInner(c: Context, asset: BuiltAsset, status: StatusCode) {
return c.res = new Response(body, { headers, status });
}
-import * as fs from "../fs.ts";
+import * as fs from "#sitegen/fs";
import type { Context, Next } from "hono";
import type { StatusCode } from "hono/utils/http-status";
import type { BuiltAsset, BuiltAssetMap, View } from "../incremental.ts";
diff --git a/framework/fs.ts b/framework/lib/fs.ts
similarity index 100%
rename from framework/fs.ts
rename to framework/lib/fs.ts
diff --git a/framework/meta.ts b/framework/lib/meta.ts
similarity index 86%
rename from framework/meta.ts
rename to framework/lib/meta.ts
index cfde26b..c0b65a9 100644
--- a/framework/meta.ts
+++ b/framework/lib/meta.ts
@@ -21,4 +21,4 @@ export interface AlternateType {
export function renderMeta({ title }: Meta): string {
return `
${esc(title)}`;
}
-import { escapeHtml as esc } from "./engine/ssr.ts";
+import { escapeHtml as esc } from "../engine/ssr.ts";
diff --git a/framework/mime.ts b/framework/lib/mime.ts
similarity index 96%
rename from framework/mime.ts
rename to framework/lib/mime.ts
index d71480b..3560a8c 100644
--- a/framework/mime.ts
+++ b/framework/lib/mime.ts
@@ -27,5 +27,5 @@ export function contentTypeFor(file: string) {
return extensions.get(file) ?? "application/octet-stream";
}
-import * as fs from "./fs.ts";
+import * as fs from "#sitegen/fs";
import * as path from "node:path";
diff --git a/framework/mime.txt b/framework/lib/mime.txt
similarity index 100%
rename from framework/mime.txt
rename to framework/lib/mime.txt
diff --git a/framework/sqlite.ts b/framework/lib/sqlite.ts
similarity index 98%
rename from framework/sqlite.ts
rename to framework/lib/sqlite.ts
index 1646932..4018c18 100644
--- a/framework/sqlite.ts
+++ b/framework/lib/sqlite.ts
@@ -94,5 +94,4 @@ export class Stmt {
}
import { DatabaseSync, StatementSync } from "node:sqlite";
-import * as fs from "./fs.ts";
import * as path from "node:path";
diff --git a/framework/typecheck.ts b/framework/typecheck.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/package-lock.json b/package-lock.json
index 805fb03..2145694 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -943,7 +943,7 @@
}
},
"node_modules/@paperclover/console": {
- "resolved": "git+https://git.paperclover.net/clo/console.git#29ae0fe6bb948723655b2d875faef75433b78ac0",
+ "resolved": "git+https://git.paperclover.net/clo/console.git#acdad1b233b5e27dd8ff817d3b8ee7f1a88519d3",
"dependencies": {
"ansi-escapes": "^7.0.0",
"chalk": "^5.4.1",
diff --git a/package.json b/package.json
index ef8de70..76abb02 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,6 @@
"#backend": "./src/backend.ts",
"#sitegen": "./framework/lib/sitegen.ts",
"#sitegen/*": "./framework/lib/*.ts",
- "#sqlite": "./framework/sqlite.ts",
"#ssr": "./framework/engine/ssr.ts",
"#ssr/jsx-dev-runtime": "./framework/engine/jsx-runtime.ts",
"#ssr/jsx-runtime": "./framework/engine/jsx-runtime.ts",
diff --git a/readme.md b/readme.md
index 533018d..37bd508 100644
--- a/readme.md
+++ b/readme.md
@@ -13,7 +13,48 @@ that assist building websites. these tools power https://paperclover.net.
- The only difference in development and production mode is hidden
source-maps and stripped assertions and `console.debug` calls. The site
you see locally is the site you see deployed.
+- Tools for building complex, content heavy web sites.
+ - Static asset serving with ETag and build-time compression.
+ - Dynamic server side rendering from within backend code.
+ - Databases with a typed SQLite wrapper.
- Built on the battle-tested Node.js runtime. Partial support for Deno and Bun.
[1]: https://next.markojs.com
+Included is `src`, which contains `paperclover.net`. Website highlights:
+
+- [Question/Answer board, custom markdown parser and components][q+a].
+- [File viewer with prefetching + client-side navigation][file].
+- [Personal, friends-only blog with password protection][friends].
+
+[q+a]: https://paperclover.net/q+a
+[file]: https://paperclover.net/file
+[friends]: https://paperclover.net/friends
+
+## Development
+
+```
+npm install
+
+# production generation
+node run generate
+
+# live development environment
+node repl
+```
+
+`repl.js` will open a read-eval-print-loop where plugin state is cached (on my
+2014 dev laptop, startup time is 600-1000ms). every file in `framework` and
+`src` besides `hot.ts` can be edited and quickly re-run. for example, to run
+`framework/generate.ts`, you can type "generate" into the shell. since
+top-level await is not supported (plugins are built on `require` as Node has
+poor module support), CLIs can include a `main` function, which is executed
+when the REPL runs it.
+
+TODO: in the future, the repl will have a "dev" command which will do the incremental
+site build + host, possibly immediatly on opening the repl.
+
+## Contributions
+
+No contributions to `src` accepted, only `framework`.
+
diff --git a/src/admin.ts b/src/admin.ts
index 96691e6..3e9c394 100644
--- a/src/admin.ts
+++ b/src/admin.ts
@@ -59,6 +59,6 @@ export function hasAdminToken(c: Context) {
return token && compareToken(token);
}
-import * as fs from "fs";
+import * as fs from "node:fs";
import type { Context, Next } from "hono";
import { serveAsset } from "#sitegen/assets";
diff --git a/src/backend.ts b/src/backend.ts
index 5cbda38..3e6c20f 100644
--- a/src/backend.ts
+++ b/src/backend.ts
@@ -30,7 +30,7 @@ async function removeDuplicateSlashes(c: Context, next: Next) {
await next();
}
-import { Hono } from "#hono";
+import { type Context, Hono, type Next } from "#hono";
import { logger } from "hono/logger";
import { trimTrailingSlash } from "hono/trailing-slash";
import * as assets from "#sitegen/assets";
diff --git a/src/q+a/models/PendingQuestion.ts b/src/q+a/models/PendingQuestion.ts
index f1a239a..4dea9a7 100644
--- a/src/q+a/models/PendingQuestion.ts
+++ b/src/q+a/models/PendingQuestion.ts
@@ -66,7 +66,7 @@ const getByDateQuery = db.prepare<[qmid: number]>(`
SELECT * FROM questions WHERE qmid = ? AND type = ${QuestionType.pending} LIMIT 1
`).as(PendingQuestion);
-import { getDb } from "#sqlite";
+import { getDb } from "#sitegen/sqlite";
import assert from "node:assert";
import { QuestionType } from "./Question.ts";
import { formatQuestionId } from "../format.ts";
diff --git a/src/q+a/models/Question.ts b/src/q+a/models/Question.ts
index 9f42994..d382955 100644
--- a/src/q+a/models/Question.ts
+++ b/src/q+a/models/Question.ts
@@ -115,4 +115,4 @@ const updateByQmidQuery = db.prepare<
`);
import { formatQuestionId } from "../format.ts";
-import { getDb } from "#sqlite";
+import { getDb } from "#sitegen/sqlite";