mirror of
https://github.com/HananoshikaYomaru/openfreebooks.git
synced 2026-11-12 20:45:52 +00:00
Refactor math chapter structure and styling: Update .gitignore to exclude specific CSS directories, enhance CONTRIBUTING.md with new guidelines for subject-specific assets, and streamline chapter content organization. Modify curriculum data to include new chapter dependencies and improve frontend logic for loading shared styles and scripts. Adjust styles for better visual consistency across math chapters.
This commit is contained in:
parent
5175811579
commit
a2679aa4de
24 changed files with 2104 additions and 530 deletions
|
|
@ -13,6 +13,18 @@ const JSON_PATH = path.join(ROOT, "data/math-curriculum.json");
|
|||
|
||||
/** slug → framework label → role */
|
||||
const COVERAGE_BY_SLUG: Record<string, Coverage> = {
|
||||
"zero-and-one": {
|
||||
Foundation: "core",
|
||||
IGCSE: "core",
|
||||
"Common Core": "core",
|
||||
Singapore: "core",
|
||||
"UK GCSE": "core",
|
||||
DSE: "related",
|
||||
IB: "related",
|
||||
"A-Level": "related",
|
||||
AP: "related",
|
||||
Gaokao: "related",
|
||||
},
|
||||
"counting-and-numbers": {
|
||||
Foundation: "core",
|
||||
IGCSE: "core",
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@ const CONTENT = join(ROOT, "content");
|
|||
const GENERATED_META = join(ROOT, "data/_generated/chapters-meta.json");
|
||||
const GENERATED_NAV = join(ROOT, "data/_generated/chapter-nav.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 GENERATED_SUBJECTS_META = join(ROOT, "data/_generated/subjects-meta.json");
|
||||
const STATIC_ROOT = join(ROOT, "static");
|
||||
const CHAPTER_CSS_DIR = join(STATIC_ROOT, "css/chapters");
|
||||
const SUBJECT_CSS_DIR = join(STATIC_ROOT, "css/subjects");
|
||||
|
||||
type ChapterMeta = {
|
||||
hasSupplement: boolean;
|
||||
|
|
@ -44,6 +47,15 @@ type ChapterNavFile = {
|
|||
byKey: Record<string, ChapterNavEntry>;
|
||||
};
|
||||
|
||||
type SubjectMeta = {
|
||||
hasCss: boolean;
|
||||
hasJs: boolean;
|
||||
};
|
||||
|
||||
type SubjectsMetaFile = {
|
||||
subjects: Record<string, SubjectMeta>;
|
||||
};
|
||||
|
||||
function parseChapterTemplate(markdown: string): boolean {
|
||||
return /template\s*=\s*["']chapter\.html["']/.test(markdown);
|
||||
}
|
||||
|
|
@ -107,6 +119,65 @@ async function compileChapterScss(
|
|||
return true;
|
||||
}
|
||||
|
||||
async function compileSubjectScss(subject: string, scssPath: string): Promise<boolean> {
|
||||
const outPath = join(SUBJECT_CSS_DIR, `${subject}.css`);
|
||||
await mkdir(SUBJECT_CSS_DIR, { recursive: true });
|
||||
const result = await $`sass ${scssPath} ${outPath} --style=compressed --no-source-map`.quiet();
|
||||
if (result.exitCode !== 0) {
|
||||
console.error(`Failed to compile ${scssPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function discoverSubjects(): Promise<Array<{ id: string; dir: string }>> {
|
||||
const subjects: Array<{ id: string; dir: string }> = [];
|
||||
let entries;
|
||||
try {
|
||||
entries = await readdir(CONTENT, { withFileTypes: true });
|
||||
} catch {
|
||||
return subjects;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
||||
subjects.push({ id: entry.name, dir: join(CONTENT, entry.name) });
|
||||
}
|
||||
return subjects;
|
||||
}
|
||||
|
||||
async function syncSubject(subject: { id: string; dir: string }): Promise<SubjectMeta> {
|
||||
const scssPath = join(subject.dir, "subject.scss");
|
||||
const tsPath = join(subject.dir, "subject.ts");
|
||||
let hasCss = false;
|
||||
let hasJs = false;
|
||||
if (existsSync(scssPath)) {
|
||||
hasCss = await compileSubjectScss(subject.id, scssPath);
|
||||
}
|
||||
if (existsSync(tsPath)) {
|
||||
hasJs = true;
|
||||
}
|
||||
return { hasCss, hasJs };
|
||||
}
|
||||
|
||||
function buildSubjectModuleRegistry(
|
||||
subjects: Array<{ id: string; dir: string; hasJs: boolean }>
|
||||
): string {
|
||||
const withJs = subjects.filter((s) => s.hasJs);
|
||||
const lines: string[] = [
|
||||
"// Auto-generated by scripts/sync-chapters.ts — do not edit.",
|
||||
"",
|
||||
"export type SubjectModule = { initSubject: (root: HTMLElement) => void };",
|
||||
"",
|
||||
"export const subjectInitLoaders: Record<string, () => Promise<SubjectModule>> = {",
|
||||
];
|
||||
for (const { id, dir } of withJs) {
|
||||
const relFromGenerated = relative(dirname(GENERATED_SUBJECT_MODULES), dir).replace(/\\/g, "/");
|
||||
lines.push(` "${id}": () => import("${relFromGenerated}/subject.ts"),`);
|
||||
}
|
||||
lines.push("};", "");
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
async function discoverChapters(): Promise<
|
||||
Array<{ subject: string; slug: string; dir: string }>
|
||||
> {
|
||||
|
|
@ -273,6 +344,22 @@ async function main() {
|
|||
const chapterNav = await buildChapterNav(chapters);
|
||||
await writeFile(GENERATED_NAV, `${JSON.stringify(chapterNav, null, 2)}\n`);
|
||||
|
||||
const subjectEntries: Array<{ id: string; dir: string; hasJs: boolean }> = [];
|
||||
const subjectsMeta: SubjectsMetaFile = { subjects: {} };
|
||||
for (const subject of await discoverSubjects()) {
|
||||
const meta = await syncSubject(subject);
|
||||
subjectsMeta.subjects[subject.id] = meta;
|
||||
subjectEntries.push({ id: subject.id, dir: subject.dir, hasJs: meta.hasJs });
|
||||
if (meta.hasCss || meta.hasJs) {
|
||||
console.log(
|
||||
`Synced subject ${subject.id} (css: ${meta.hasCss ? "yes" : "no"}, js: ${meta.hasJs ? "yes" : "no"})`
|
||||
);
|
||||
}
|
||||
}
|
||||
await writeFile(GENERATED_SUBJECTS_META, `${JSON.stringify(subjectsMeta, null, 2)}\n`);
|
||||
await mkdir(dirname(GENERATED_SUBJECT_MODULES), { recursive: true });
|
||||
await writeFile(GENERATED_SUBJECT_MODULES, buildSubjectModuleRegistry(subjectEntries));
|
||||
|
||||
console.log(`Synced ${chapters.length} chapter(s).`);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue