This commit is contained in:
chloe caruso 2025-07-07 20:58:02 -07:00
parent f1b1c650ce
commit ea5f2bc325
48 changed files with 5217 additions and 5177 deletions

View file

@ -16,7 +16,10 @@
pkgs.nodejs_24 # runtime
pkgs.deno # formatter
(pkgs.ffmpeg.override {
withOpus = true;
withSvtav1 = true;
withJxl = true;
withWebp = true;
})
];
};

View file

@ -96,7 +96,9 @@ Module._resolveFilename = (...args) => {
try {
return require.resolve(replacedPath, { paths: [projectSrc] });
} catch (err: any) {
if (err.code === "MODULE_NOT_FOUND" && (err?.requireStack?.length ?? 0) <= 1) {
if (
err.code === "MODULE_NOT_FOUND" && (err?.requireStack?.length ?? 0) <= 1
) {
err.message.replace(replacedPath, args[0]);
}
}

View file

@ -107,7 +107,7 @@ export class Queue<T, R> {
try {
active.unshift(spinner);
bar.props = { active };
console.log(this.#name + ": " + itemText);
// console.log(this.#name + ": " + itemText);
const result = await this.#fn(args, spinner);
this.#done++;
return result;
@ -116,6 +116,7 @@ export class Queue<T, R> {
(err as any).job = itemText;
}
this.#errors.push(err);
console.error(util.inspect(err, false, Infinity, true));
throw err;
} finally {
active.splice(active.indexOf(spinner), 1);
@ -295,3 +296,4 @@ import { Progress } from "@paperclover/console/Progress";
import { Spinner } from "@paperclover/console/Spinner";
import * as path from "node:path";
import process from "node:process";
import * as util from "node:util";

View file

@ -4,27 +4,27 @@ this repository contains clover's "sitegen" framework, which is a set of tools
that assist building websites. these tools power https://paperclover.net.
- **HTML "Server Side Rendering") engine written from scratch.** (~500 lines)
- A more practical JSX runtime (`class` instead of `className`, built-in
`clsx`, `html()` helper over `dangerouslySetInnerHTML` prop, etc).
- Integration with [Marko][1] for concisely written components.
- TODO: MDX-like compiler for content-heavy pages like blogs.
- Different languages can be used at the same time. Supports
`async function` components, `<Suspense />`, and custom extensions.
- A more practical JSX runtime (`class` instead of `className`, built-in
`clsx`, `html()` helper over `dangerouslySetInnerHTML` prop, etc).
- Integration with [Marko][1] for concisely written components.
- TODO: MDX-like compiler for content-heavy pages like blogs.
- Different languages can be used at the same time. Supports `async function`
components, `<Suspense />`, and custom extensions.
- **Incremental static site generator and build system.**
- Build entire production site at start, incremental updates when pages
change; Build system state survives coding sessions.
- The only difference in development and production mode is hidden
source-maps and stripped `console.debug` calls. The site you
see locally is the same site you see deployed.
- (TODO) Tests, Lints, and Type-checking is run alongside, and only re-runs
checks when the files change. For example, changing a component re-tests
only pages that use that component and re-lints only the changed file.
- Build entire production site at start, incremental updates when pages
change; Build system state survives coding sessions.
- The only difference in development and production mode is hidden source-maps
and stripped `console.debug` calls. The site you see locally is the same
site you see deployed.
- (TODO) Tests, Lints, and Type-checking is run alongside, and only re-runs
checks when the files change. For example, changing a component re-tests
only pages that use that component and re-lints only the changed file.
- **Integrated libraries for building complex, content heavy web sites.**
- Static asset serving with ETag and build-time compression.
- Dynamicly rendered pages with static client. (`#import "#sitegen/view"`)
- Databases with a typed SQLite wrapper. (`import "#sitegen/sqlite"`)
- TODO: Meta and Open Graph generation. (`export const meta`)
- TODO: Font subsetting tools to reduce bytes downloaded by fonts.
- Static asset serving with ETag and build-time compression.
- Dynamicly rendered pages with static client. (`#import "#sitegen/view"`)
- Databases with a typed SQLite wrapper. (`import "#sitegen/sqlite"`)
- TODO: Meta and Open Graph generation. (`export const meta`)
- TODO: Font subsetting tools to reduce bytes downloaded by fonts.
- **Built on the battle-tested Node.js runtime.**
[1]: https://next.markojs.com
@ -42,6 +42,7 @@ Included is `src`, which contains `paperclover.net`. Website highlights:
## Development
minimum system requirements:
- a cpu with at least 1 core.
- random access memory.
- windows 7 or later, macos, or other operating system.
@ -73,4 +74,3 @@ open a shell with all needed system dependencies.
## Contributions
No contributions to `src` accepted, only `framework`.

2
run.js
View file

@ -12,7 +12,7 @@ if (!zlib.zstdCompress) {
: null;
globalThis.console.error(
`sitegen depends on a node.js-compatibile runtime that supports zstd compression\n` +
`sitegen depends on a node.js-compatibile runtime\n` +
`this is node.js version ${process.version}${
brand ? ` (${brand})` : ""
}\n\n` +

View file

@ -252,6 +252,7 @@ interface Process {
enable?: boolean;
include: Set<string>;
depends?: string[];
version?: number;
/* Perform an action. */
run(args: ProcessFileArgs): Promise<void>;
/* Should detect if `run` was never even run before before undoing state */
@ -384,6 +385,7 @@ const procImageSubsets: Process = {
name: "encode image subsets",
include: rules.extsImage,
depends: ["calculate dimensions"],
version: 2,
async run({ absPath, mediaFile, spin }) {
const { width, height } = UNWRAP(mediaFile.parseDimensions());
const targetSizes = transcodeRules.imageSizes.filter((w) => w < width);
@ -485,6 +487,7 @@ const procVideos = transcodeRules.videoFormats.map<Process>((preset) => ({
title: fakeProgress.text,
progress: fakeProgress,
args,
cwd: base,
});
return await collectFiles();
} catch (err) {
@ -522,7 +525,10 @@ const processors = [
// Create a unique key.
hash: new Uint16Array(
crypto.createHash("sha1")
.update(process.run.toString())
.update(
process.run.toString() +
(process.version ? String(process.version) : ""),
)
.digest().buffer,
).reduce((a, b) => a ^ b),
depends: (process.depends ?? []).map((depend) => {

View file

@ -135,10 +135,7 @@ function highlightLines({
export const getRegistry = async.once(async () => {
const wasmBin = await fs.readFile(
path.join(
import.meta.dirname,
"../node_modules/vscode-oniguruma/release/onig.wasm",
),
require.resolve("vscode-oniguruma/release/onig.wasm"),
);
await oniguruma.loadWASM(wasmBin);

View file

@ -10,7 +10,7 @@ db.table(
create table if not exists asset_ref_files (
file text not null,
id integer not null,
foreign key (id) references asset_refs(id)
foreign key (id) references asset_refs(id) ON DELETE CASCADE
);
create index asset_ref_files_id on asset_ref_files(id);
`,

View file

@ -56,11 +56,15 @@ export const extsImage = new Set([
".jpg",
".jpeg",
".png",
".gif",
".webp",
".avif",
".heic",
]);
/** These files show an image embed, but aren't optimized */
export const extsImageLike = new Set([
...extsImage,
".svg",
".gif",
]);
/** These files populate `duration` using `ffprobe` */

View file

@ -99,14 +99,38 @@ export const imagePresets = [
"6",
],
},
// TODO: avif
{
ext: ".avif",
args: [
"-c:v",
"libaom-av1",
"-crf",
"30",
"-pix_fmt",
"yuv420p10le",
],
},
{
ext: ".jxl",
args: ["-c:v", "libjxl", "-distance", "0.8", "-effort", "9"],
args: [
"-c:v",
"libjxl",
"-distance",
"0.8",
"-effort",
"9",
"-update",
"-frames:v",
"1",
],
},
];
export function getVideoArgs(preset: VideoEncodePreset, outbase: string, input: string[]) {
export function getVideoArgs(
preset: VideoEncodePreset,
outbase: string,
input: string[],
) {
const cmd = [...input];
if (preset.codec === "av1") {

View file

@ -27,12 +27,12 @@ li a {
border-radius: 4px;
}
li a:hover {
background-color: rgba(255,255,255,0.2);
background-color: rgba(255, 255, 255, 0.2);
font-weight: bold;
text-decoration: none!important;
text-decoration: none !important;
}
.dir a {
color: #99eeFF
color: #99eeff;
}
.ext {
opacity: 0.5;

View file

@ -118,4 +118,3 @@ code {
font-family: "rmo", monospace;
font-size: inherit;
}

View file

@ -1,4 +1,4 @@
body,html {
body, html {
overflow: hidden;
}
h1 {
@ -41,7 +41,7 @@ footer h2 {
header h2, header em, footer h2, footer em {
display: inline-block;
}
header em, footer em {
margin-left: 16px!important;
text-align: right;
header em, footer em {
margin-left: 16px !important;
text-align: right;
}

View file

@ -3,7 +3,9 @@
<title>paper clover</title>
</head>
<body bgcolor="black" style="word-wrap: initial">
<main style="display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh">
<main
style="display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh"
>
<div>
<p style="margin: 0.5rem 0">
<a
@ -56,7 +58,9 @@
<font color="#FF8147">feed</font>
</a>
</p>
<h1 style="margin: -1.5rem 0 3rem 0; font-size: 7rem; font-weight: 400; font-family: times">
<h1
style="margin: -1.5rem 0 3rem 0; font-size: 7rem; font-weight: 400; font-family: times"
>
<font color="#B8E1FF">paper</font>
<font color="#E8F4FF">clover</font>
</h1>

View file

@ -22,4 +22,3 @@
margin-top: -1px;
padding-bottom: 2px;
}