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";