mirror of
https://github.com/HananoshikaYomaru/openfreebooks.git
synced 2026-11-13 13:05:51 +00:00
Update contributing guidelines and enhance build scripts: Add detailed instructions for build modes in CONTRIBUTING.md, clarifying the use of incremental and full rebuilds. Revise package.json to streamline build commands and introduce new dev scripts for better development workflow. Remove deprecated integer arithmetic chapter files and update curriculum JSON to reflect the latest structure. Enhance Pythagorean theorem content with a new vocabulary section for improved clarity.
This commit is contained in:
parent
669fbd59fa
commit
30b4f4cf53
16 changed files with 496 additions and 291 deletions
206
scripts/build-smart.ts
Normal file
206
scripts/build-smart.ts
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
import { existsSync } from "node:fs";
|
||||
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { $ } from "bun";
|
||||
import { fingerprintPaths } from "./build-change-detector";
|
||||
|
||||
const ROOT = join(import.meta.dir, "..");
|
||||
const CACHE_PATH = join(ROOT, "data/_generated/build-smart-cache.json");
|
||||
const GENERATED_WIDGETS = join(ROOT, "frontend/src/generated/chapter-widgets.ts");
|
||||
const GENERATED_SUBJECT_MODULES = join(ROOT, "frontend/src/generated/subject-modules.ts");
|
||||
const BUNDLE_PATH = join(ROOT, "themes/openfreebooks/static/js/bundle.js");
|
||||
const PUBLIC_INDEX = join(ROOT, "public/index.html");
|
||||
const CHAPTER_LASTMOD_PATH = join(ROOT, "data/_generated/chapter-lastmod.json");
|
||||
const PAGEFIND_DIR = join(ROOT, "static/pagefind");
|
||||
|
||||
type BuildCache = {
|
||||
version: 1;
|
||||
fingerprints: {
|
||||
syncInputs: string;
|
||||
jsInputs: string;
|
||||
siteInputs: string;
|
||||
metadataInputs: string;
|
||||
generatedModules: string;
|
||||
};
|
||||
};
|
||||
|
||||
async function listCurriculumFiles(): Promise<string[]> {
|
||||
const dataDir = join(ROOT, "data");
|
||||
const entries = await readdir(dataDir, { withFileTypes: true });
|
||||
return entries
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
||||
.map((entry) => join(dataDir, entry.name));
|
||||
}
|
||||
|
||||
async function listContentJsInputs(): Promise<string[]> {
|
||||
const contentDir = join(ROOT, "content");
|
||||
const inputs: string[] = [];
|
||||
const queue = [contentDir];
|
||||
while (queue.length) {
|
||||
const current = queue.pop();
|
||||
if (!current) continue;
|
||||
const entries = await readdir(current, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith(".")) continue;
|
||||
const abs = join(current, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
queue.push(abs);
|
||||
continue;
|
||||
}
|
||||
if (entry.name === "subject.ts" || abs.includes("/widgets/")) {
|
||||
inputs.push(abs);
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
||||
async function listStaticSourceInputs(): Promise<string[]> {
|
||||
const staticDir = join(ROOT, "static");
|
||||
if (!existsSync(staticDir)) return [];
|
||||
const inputs: string[] = [];
|
||||
const queue = [staticDir];
|
||||
while (queue.length) {
|
||||
const current = queue.pop();
|
||||
if (!current) continue;
|
||||
const entries = await readdir(current, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith(".")) continue;
|
||||
const abs = join(current, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
if (abs === join(staticDir, "pagefind")) continue;
|
||||
if (abs === join(staticDir, "chapters")) continue;
|
||||
if (abs === join(staticDir, "css")) continue;
|
||||
queue.push(abs);
|
||||
continue;
|
||||
}
|
||||
inputs.push(abs);
|
||||
}
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
||||
async function readCache(): Promise<BuildCache | null> {
|
||||
if (!existsSync(CACHE_PATH)) return null;
|
||||
try {
|
||||
const parsed = JSON.parse(await readFile(CACHE_PATH, "utf8")) as BuildCache;
|
||||
if (parsed.version !== 1) return null;
|
||||
return parsed;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function runStep(command: string) {
|
||||
const result = await $`bash -lc ${command}`;
|
||||
if (result.exitCode !== 0) {
|
||||
process.exit(result.exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
async function runFullBuild() {
|
||||
await runStep("bun run check:static-html");
|
||||
await runStep("bun run validate:curriculum");
|
||||
await runStep("rm -f themes/openfreebooks/static/main.css public/main.css");
|
||||
await runStep("bun run sync:chapters -- --full");
|
||||
await runStep("vite build");
|
||||
await runStep("bun run generate:site-metadata");
|
||||
await runStep("zola build");
|
||||
await runStep("bun run index:search");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.env.CI === "true") {
|
||||
await runFullBuild();
|
||||
return;
|
||||
}
|
||||
|
||||
const curriculumFiles = await listCurriculumFiles();
|
||||
const contentJsInputs = await listContentJsInputs();
|
||||
const staticSourceInputs = await listStaticSourceInputs();
|
||||
const fingerprints = {
|
||||
syncInputs: await fingerprintPaths(ROOT, [
|
||||
join(ROOT, "content"),
|
||||
join(ROOT, "scripts/sync-chapters.ts"),
|
||||
join(ROOT, "scripts/build-change-detector.ts"),
|
||||
...curriculumFiles,
|
||||
]),
|
||||
jsInputs: await fingerprintPaths(ROOT, [
|
||||
join(ROOT, "frontend"),
|
||||
join(ROOT, "vite.config.ts"),
|
||||
...contentJsInputs,
|
||||
]),
|
||||
siteInputs: await fingerprintPaths(ROOT, [
|
||||
join(ROOT, "content"),
|
||||
join(ROOT, "themes/openfreebooks/templates"),
|
||||
join(ROOT, "themes/openfreebooks/sass"),
|
||||
join(ROOT, "zola.toml"),
|
||||
...staticSourceInputs,
|
||||
]),
|
||||
metadataInputs: await fingerprintPaths(ROOT, [
|
||||
join(ROOT, "content"),
|
||||
join(ROOT, "zola.toml"),
|
||||
join(ROOT, "scripts/generate-site-metadata.ts"),
|
||||
...curriculumFiles,
|
||||
]),
|
||||
generatedModules: "",
|
||||
};
|
||||
|
||||
const previous = await readCache();
|
||||
const previousFingerprints = previous?.fingerprints;
|
||||
const firstRun = !previous;
|
||||
const hasGeneratedModules = existsSync(GENERATED_WIDGETS) && existsSync(GENERATED_SUBJECT_MODULES);
|
||||
const needsSync =
|
||||
firstRun || !hasGeneratedModules || previousFingerprints?.syncInputs !== fingerprints.syncInputs;
|
||||
|
||||
await runStep("bun run check:static-html");
|
||||
await runStep("bun run validate:curriculum");
|
||||
|
||||
if (needsSync) {
|
||||
await runStep("bun run sync:chapters");
|
||||
}
|
||||
|
||||
fingerprints.generatedModules = await fingerprintPaths(ROOT, [GENERATED_WIDGETS, GENERATED_SUBJECT_MODULES]);
|
||||
|
||||
const needsJs =
|
||||
firstRun ||
|
||||
!existsSync(BUNDLE_PATH) ||
|
||||
previousFingerprints?.jsInputs !== fingerprints.jsInputs ||
|
||||
previousFingerprints?.generatedModules !== fingerprints.generatedModules;
|
||||
if (needsJs) {
|
||||
await runStep("rm -f themes/openfreebooks/static/main.css public/main.css");
|
||||
await runStep("vite build");
|
||||
}
|
||||
|
||||
const needsMetadata =
|
||||
firstRun ||
|
||||
!existsSync(CHAPTER_LASTMOD_PATH) ||
|
||||
previousFingerprints?.metadataInputs !== fingerprints.metadataInputs;
|
||||
if (needsMetadata) {
|
||||
await runStep("bun run generate:site-metadata");
|
||||
}
|
||||
|
||||
const needsSite =
|
||||
firstRun ||
|
||||
!existsSync(PUBLIC_INDEX) ||
|
||||
needsJs ||
|
||||
needsMetadata ||
|
||||
previousFingerprints?.siteInputs !== fingerprints.siteInputs;
|
||||
if (needsSite) {
|
||||
await runStep("zola build");
|
||||
}
|
||||
|
||||
if (needsSite || !existsSync(PAGEFIND_DIR)) {
|
||||
await runStep("bun run index:search");
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[build-smart] sync=${needsSync} js=${needsJs} metadata=${needsMetadata} site=${needsSite} firstRun=${firstRun}`
|
||||
);
|
||||
|
||||
const nextCache: BuildCache = { version: 1, fingerprints };
|
||||
await mkdir(dirname(CACHE_PATH), { recursive: true });
|
||||
await writeFile(CACHE_PATH, `${JSON.stringify(nextCache, null, 2)}\n`);
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Add table
Add a link
Reference in a new issue