Enhance package.json with a new script for scanning output size and update index.html to improve the hero section's heading with a new design and messaging.

This commit is contained in:
Hananoshika Yomaru 2026-05-19 01:21:41 -07:00
parent 0d36f3a2d6
commit 3f4a7a9e73
3 changed files with 112 additions and 2 deletions

View file

@ -6,7 +6,8 @@
"scripts": {
"build:js": "vite build",
"build": "bun run build:js && zola build",
"dev:js": "vite build --watch"
"dev:js": "vite build --watch",
"scan:size": "bun scripts/scan-output-size.ts"
},
"dependencies": {
"katex": "^0.16.47",

102
scripts/scan-output-size.ts Normal file
View file

@ -0,0 +1,102 @@
import { readdir, stat } from "node:fs/promises";
import { join, relative } from "node:path";
const OUTPUT_DIRS = [
{ label: "Zola (deploy)", path: "public" },
{ label: "Vite JS/CSS", path: "themes/openfreebooks/static/js" },
] as const;
const TOP_FILES = 15;
type FileEntry = { path: string; bytes: number };
async function collectFiles(
dir: string,
root: string,
entries: FileEntry[],
): Promise<void> {
let items;
try {
items = await readdir(dir, { withFileTypes: true });
} catch {
return;
}
for (const item of items) {
const fullPath = join(dir, item.name);
if (item.isDirectory()) {
await collectFiles(fullPath, root, entries);
continue;
}
if (!item.isFile()) continue;
const { size } = await stat(fullPath);
entries.push({ path: relative(root, fullPath), bytes: size });
}
}
function formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
}
function padEnd(value: string, width: number): string {
return value.length >= width ? value : value + " ".repeat(width - value.length);
}
async function scanDir(label: string, root: string) {
const entries: FileEntry[] = [];
await collectFiles(root, root, entries);
if (entries.length === 0) {
console.log(`\n${label} (${root}/)`);
console.log(" (missing or empty — run `bun run build` first)\n");
return null;
}
const total = entries.reduce((sum, e) => sum + e.bytes, 0);
entries.sort((a, b) => b.bytes - a.bytes);
console.log(`\n${label} (${root}/)`);
console.log(` files: ${entries.length}`);
console.log(` total: ${formatBytes(total)} (${total.toLocaleString()} bytes)`);
const labelWidth = Math.min(
52,
Math.max(...entries.slice(0, TOP_FILES).map((e) => e.path.length), 4),
);
console.log(`\n largest files:`);
for (const entry of entries.slice(0, TOP_FILES)) {
console.log(
` ${padEnd(entry.path, labelWidth)} ${padEnd(formatBytes(entry.bytes), 10)} ${entry.bytes.toLocaleString()} B`,
);
}
return { label, root, total, fileCount: entries.length, entries };
}
async function main() {
const cwd = process.cwd();
console.log(`Output size scan — ${cwd}`);
const results = [];
for (const { label, path } of OUTPUT_DIRS) {
const result = await scanDir(label, join(cwd, path));
if (result) results.push(result);
}
if (results.length > 0) {
const grandTotal = results.reduce((sum, r) => sum + r.total, 0);
console.log("\n---");
console.log(
`Combined tracked output: ${formatBytes(grandTotal)} (${grandTotal.toLocaleString()} bytes)`,
);
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

View file

@ -10,7 +10,14 @@
<section class="hero">
<div class="container">
<div class="hero-content">
<h1>Open textbooks. Free to read. Open to improve.</h1>
<h1>
Future of textbooks.
<span style="display: inline-block; border: 2px solid #9a6b2e; border-radius: 50%; padding: 0 0.4em; margin: 0 0.1em; line-height: 1; background: rgba(154,107,46,0.13); color: #9a6b2e;">Free</span>
to read.
<span style="display: inline-block; border: 2px solid #29a05d; border-radius: 50%; padding: 0 0.4em; margin: 0 0.1em; line-height: 1; background: rgba(41,160,93,0.13); color: #29a05d;">Open</span>
to improve.
</h1>
<p class="hero-lead">
High-quality learning materials for elementary school through university — on the web, at no cost.
</p>