explico 0.11.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1292 -1
- package/README.md +91 -13
- package/dist/compiler/astro.d.ts +11 -4
- package/dist/compiler/astro.js +48 -15
- package/dist/compiler/cli.d.ts +27 -12
- package/dist/compiler/cli.js +96 -25
- package/dist/compiler/collections.d.ts +33 -2
- package/dist/compiler/collections.js +91 -19
- package/dist/compiler/content-schema.d.ts +31 -0
- package/dist/compiler/content-schema.js +82 -0
- package/dist/compiler/content-schemas.d.ts +422 -0
- package/dist/compiler/content-schemas.js +229 -0
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +30 -2
- package/dist/compiler/exports/combined-html.d.ts +97 -0
- package/dist/compiler/exports/combined-html.js +492 -0
- package/dist/compiler/exports/download-index.d.ts +9 -0
- package/dist/compiler/exports/download-index.js +33 -0
- package/dist/compiler/exports/export-chapters.d.ts +98 -0
- package/dist/compiler/exports/export-chapters.js +164 -0
- package/dist/compiler/exports/export-pages.d.ts +48 -0
- package/dist/compiler/exports/export-pages.js +22 -0
- package/dist/compiler/exports/html-tree.d.ts +22 -0
- package/dist/compiler/exports/html-tree.js +47 -0
- package/dist/compiler/exports/latex/capture.d.ts +106 -0
- package/dist/compiler/exports/latex/capture.js +435 -0
- package/dist/compiler/exports/latex/compile.d.ts +73 -0
- package/dist/compiler/exports/latex/compile.js +115 -0
- package/dist/compiler/exports/latex/document.d.ts +102 -0
- package/dist/compiler/exports/latex/document.js +349 -0
- package/dist/compiler/exports/latex/explico.cls +415 -0
- package/dist/compiler/exports/latex/numbering.d.ts +18 -0
- package/dist/compiler/exports/latex/numbering.js +57 -0
- package/dist/compiler/exports/latex/serialize.d.ts +112 -0
- package/dist/compiler/exports/latex/serialize.js +606 -0
- package/dist/compiler/exports/latex-export.d.ts +44 -0
- package/dist/compiler/exports/latex-export.js +169 -0
- package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
- package/dist/compiler/exports/markdown/serialize.js +527 -0
- package/dist/compiler/exports/markdown-export.d.ts +39 -0
- package/dist/compiler/exports/markdown-export.js +240 -0
- package/dist/compiler/exports/standalone-html.d.ts +15 -5
- package/dist/compiler/exports/standalone-html.js +23 -51
- package/dist/compiler/exports/static-tree.d.ts +253 -0
- package/dist/compiler/exports/static-tree.js +625 -0
- package/dist/compiler/exports/zip.d.ts +16 -0
- package/dist/compiler/exports/zip.js +72 -0
- package/dist/compiler/lesson-derivation.d.ts +8 -2
- package/dist/compiler/lesson-derivation.js +6 -7
- package/dist/compiler/manifest.d.ts +115 -8
- package/dist/compiler/manifest.js +221 -39
- package/dist/compiler/render-diagnostics.d.ts +65 -0
- package/dist/compiler/render-diagnostics.js +56 -0
- package/dist/compiler/resolution-report.d.ts +3 -1
- package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
- package/dist/curriculum/validation.d.ts +9 -3
- package/dist/curriculum/validation.js +35 -17
- package/dist/reference/base-abbreviations.d.mts +2 -2
- package/dist/reference/base-abbreviations.mjs +2 -2
- package/dist/reference/base-operators.d.mts +8 -2
- package/dist/reference/base-operators.mjs +8 -10
- package/dist/reference/base-roman-labels.d.mts +4 -3
- package/dist/reference/base-roman-labels.mjs +4 -3
- package/dist/reference/citation-format.d.mts +35 -0
- package/dist/reference/citation-format.mjs +74 -0
- package/dist/reference/consistency.d.ts +5 -9
- package/dist/reference/consistency.js +34 -38
- package/dist/reference/curriculum-alignment.js +3 -24
- package/dist/reference/derived-entries.d.ts +45 -0
- package/dist/reference/derived-entries.js +50 -0
- package/dist/reference/derived-notation.d.mts +125 -0
- package/dist/reference/derived-notation.mjs +121 -0
- package/dist/reference/diagram-fit.d.ts +45 -0
- package/dist/reference/diagram-fit.js +54 -0
- package/dist/reference/diagram-layout.d.ts +59 -10
- package/dist/reference/diagram-layout.js +85 -24
- package/dist/reference/diagrams-validate.d.ts +1 -1
- package/dist/reference/equations-validate.d.ts +1 -1
- package/dist/reference/equations-validate.js +26 -1
- package/dist/reference/equations.d.mts +27 -0
- package/dist/reference/equations.mjs +120 -22
- package/dist/reference/examples-validate.d.ts +1 -1
- package/dist/reference/figure-fit.d.ts +70 -0
- package/dist/reference/figure-fit.js +60 -0
- package/dist/reference/figure-pictures.d.ts +27 -0
- package/dist/reference/figure-pictures.js +49 -0
- package/dist/reference/figure-symbols.d.ts +32 -0
- package/dist/reference/figure-symbols.js +55 -0
- package/dist/reference/figures-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-abbreviations.js +1 -1
- package/dist/reference/gate-course-rules.d.ts +4 -3
- package/dist/reference/gate-course-rules.js +9 -4
- package/dist/reference/gate-heading-length.d.ts +2 -0
- package/dist/reference/gate-heading-length.js +7 -5
- package/dist/reference/gate-heading-structure.js +26 -0
- package/dist/reference/gate-indices.d.ts +35 -0
- package/dist/reference/gate-indices.js +299 -0
- package/dist/reference/gate-math.d.ts +32 -2
- package/dist/reference/gate-math.js +68 -12
- package/dist/reference/gate-mdx-expression.d.ts +17 -0
- package/dist/reference/gate-mdx-expression.js +29 -0
- package/dist/reference/gate-non-chapter-pages.d.ts +39 -0
- package/dist/reference/gate-non-chapter-pages.js +156 -0
- package/dist/reference/gate-part-pages.d.ts +4 -2
- package/dist/reference/gate-part-pages.js +2 -1
- package/dist/reference/gate-prop-text.d.ts +28 -0
- package/dist/reference/gate-prop-text.js +106 -0
- package/dist/reference/gate-prose-notation.d.ts +3 -1
- package/dist/reference/gate-prose-notation.js +4 -2
- package/dist/reference/gate-retired-keys.d.ts +35 -0
- package/dist/reference/gate-retired-keys.js +58 -0
- package/dist/reference/gloss.d.ts +2 -0
- package/dist/reference/katex-measure.d.ts +17 -0
- package/dist/reference/katex-measure.js +223 -0
- package/dist/reference/keyed-float-validate.js +12 -0
- package/dist/reference/keyed-jsx-floats.d.mts +2 -2
- package/dist/reference/keyed-jsx-floats.mjs +11 -14
- package/dist/reference/lab-katex-validate.d.ts +43 -0
- package/dist/reference/lab-katex-validate.js +96 -0
- package/dist/reference/lab-math-scope.d.ts +1 -0
- package/dist/reference/lab-math-scope.js +13 -2
- package/dist/reference/lint-ignore.js +1 -1
- package/dist/reference/math-glyphs.d.mts +94 -2
- package/dist/reference/math-glyphs.mjs +851 -46
- package/dist/reference/mixed-content.d.ts +8 -3
- package/dist/reference/mixed-content.js +4 -2
- package/dist/reference/page-notation.d.ts +39 -7
- package/dist/reference/page-notation.js +62 -10
- package/dist/reference/raw-floats.mjs +37 -13
- package/dist/reference/references.d.ts +11 -1
- package/dist/reference/references.js +6 -0
- package/dist/reference/registry.js +115 -11
- package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
- package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
- package/dist/reference/rehype-heading-anchors.mjs +11 -1
- package/dist/reference/remark-citation.mjs +0 -0
- package/dist/reference/remark-notation.mjs +275 -23
- package/dist/reference/render-lab-math.d.ts +13 -2
- package/dist/reference/render-lab-math.js +18 -6
- package/dist/reference/sections-validate.d.ts +28 -3
- package/dist/reference/sections-validate.js +31 -4
- package/dist/reference/sections.d.mts +24 -6
- package/dist/reference/sections.mjs +92 -17
- package/dist/reference/tables-validate.d.ts +1 -1
- package/dist/reference/tables-validate.js +24 -1
- package/dist/reference/text-measure.d.ts +10 -0
- package/dist/reference/text-measure.js +41 -0
- package/dist/reference/text-widths.d.ts +10 -0
- package/dist/reference/text-widths.js +1238 -0
- package/dist/reference/types.d.ts +64 -8
- package/dist/rules/catalog.js +223 -32
- package/dist/rules/in-effect.d.ts +1 -1
- package/dist/rules/rules.schema.json +100 -27
- package/dist/rules/settings.d.ts +1 -1
- package/dist/rules/settings.js +7 -0
- package/dist/session/PreferenceStore.d.ts +18 -1
- package/dist/session/PreferenceStore.js +29 -1
- package/docs/authoring/README.md +33 -21
- package/docs/authoring/agents.md +38 -7
- package/docs/authoring/components.md +115 -10
- package/docs/authoring/migrating.md +878 -0
- package/docs/authoring/notation.md +285 -30
- package/docs/authoring/retired.md +1 -0
- package/docs/authoring/structure-rules.md +84 -45
- package/docs/authoring/structure.md +67 -2
- package/docs/authoring/visualizations.md +167 -25
- package/package.json +12 -7
- package/src/compiler/astro.ts +72 -21
- package/src/compiler/cli.ts +143 -35
- package/src/compiler/collections.ts +158 -27
- package/src/compiler/content-schema.ts +108 -0
- package/src/compiler/content-schemas.ts +391 -0
- package/src/compiler/course-config.ts +86 -15
- package/src/compiler/exports/combined-html.ts +669 -0
- package/src/compiler/exports/download-index.ts +36 -0
- package/src/compiler/exports/export-chapters.ts +233 -0
- package/src/compiler/exports/export-pages.ts +44 -0
- package/src/compiler/exports/html-tree.ts +62 -0
- package/src/compiler/exports/latex/capture.ts +651 -0
- package/src/compiler/exports/latex/compile.ts +168 -0
- package/src/compiler/exports/latex/document.ts +479 -0
- package/src/compiler/exports/latex/explico.cls +415 -0
- package/src/compiler/exports/latex/numbering.ts +74 -0
- package/src/compiler/exports/latex/serialize.ts +787 -0
- package/src/compiler/exports/latex/texlive-packages.txt +76 -0
- package/src/compiler/exports/latex-export.ts +255 -0
- package/src/compiler/exports/markdown/serialize.ts +632 -0
- package/src/compiler/exports/markdown-export.ts +329 -0
- package/src/compiler/exports/standalone-html.ts +39 -70
- package/src/compiler/exports/static-tree.ts +944 -0
- package/src/compiler/exports/zip.ts +88 -0
- package/src/compiler/lesson-derivation.ts +13 -8
- package/src/compiler/manifest.ts +387 -40
- package/src/compiler/render-diagnostics.ts +111 -0
- package/src/components/CourseContents.astro +6 -0
- package/src/components/HoverPanel.astro +204 -6
- package/src/components/PartOverview.astro +18 -9
- package/src/components/abbreviation/AbbreviationGlossary.astro +6 -2
- package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
- package/src/components/assessments/AssessmentRunner.tsx +7 -1
- package/src/components/assessments/AssessmentSet.astro +14 -1
- package/src/components/block-heading.ts +38 -0
- package/src/components/card-arrow.ts +18 -4
- package/src/components/card-carry.ts +406 -0
- package/src/components/card-drag.ts +1 -30
- package/src/components/card-fetch.ts +87 -0
- package/src/components/card-manager.ts +1222 -38
- package/src/components/card-memory.ts +5 -1
- package/src/components/card-placement.ts +211 -13
- package/src/components/card-side.ts +281 -31
- package/src/components/citation/CitationLayer.astro +45 -40
- package/src/components/diagram/Diagram.astro +123 -44
- package/src/components/disclosure/print-as-shown.ts +42 -6
- package/src/components/figure/Figure.astro +17 -1
- package/src/components/figure/FloatCaption.astro +4 -17
- package/src/components/figure/figure-state.ts +26 -17
- package/src/components/float-toolbar.ts +274 -0
- package/src/components/memo/Memo.astro +21 -10
- package/src/components/memo/ShortNote.astro +4 -0
- package/src/components/notation/NotationGlossary.astro +22 -17
- package/src/components/notation/NotationLayer.astro +300 -79
- package/src/components/notation/NotationMath.astro +8 -1
- package/src/components/notation/render-notation-math.ts +51 -5
- package/src/components/readable-text.ts +25 -0
- package/src/components/reference/FloatRefLayer.astro +162 -89
- package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
- package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
- package/src/components/starlight/LayoutPageSidebar.astro +17 -3
- package/src/components/starlight/LayoutSocialIcons.astro +79 -29
- package/src/components/starlight/LessonExportActions.astro +384 -310
- package/src/components/starlight/LessonFooter.astro +66 -8
- package/src/components/starlight/PrintController.astro +28 -0
- package/src/components/starlight/TextSizeControl.astro +137 -0
- package/src/components/starlight/print-controller.ts +380 -0
- package/src/components/starlight/section-summary.ts +13 -5
- package/src/components/table/Table.astro +14 -22
- package/src/components/table/table-truncation.ts +15 -0
- package/src/content-config.ts +20 -312
- package/src/curriculum/validation.ts +72 -24
- package/src/reference/base-abbreviations.mjs +2 -2
- package/src/reference/base-operators.mjs +8 -10
- package/src/reference/base-roman-labels.mjs +4 -3
- package/src/reference/citation-format.mjs +74 -0
- package/src/reference/consistency.ts +47 -61
- package/src/reference/curriculum-alignment.ts +4 -41
- package/src/reference/derived-entries.ts +68 -0
- package/src/reference/derived-notation.mjs +121 -0
- package/src/reference/diagram-fit.ts +104 -0
- package/src/reference/diagram-layout.ts +182 -51
- package/src/reference/diagrams-validate.ts +2 -1
- package/src/reference/equations-validate.ts +41 -2
- package/src/reference/equations.mjs +120 -22
- package/src/reference/examples-validate.ts +1 -0
- package/src/reference/figure-fit.ts +139 -0
- package/src/reference/figure-pictures.ts +96 -0
- package/src/reference/figure-symbols.ts +117 -0
- package/src/reference/figures-validate.ts +2 -1
- package/src/reference/gate-abbreviations.ts +1 -1
- package/src/reference/gate-course-rules.ts +22 -7
- package/src/reference/gate-heading-length.ts +25 -7
- package/src/reference/gate-heading-structure.ts +40 -1
- package/src/reference/gate-indices.ts +470 -0
- package/src/reference/gate-math.ts +102 -13
- package/src/reference/gate-mdx-expression.ts +45 -0
- package/src/reference/gate-non-chapter-pages.ts +241 -0
- package/src/reference/gate-part-pages.ts +1 -1
- package/src/reference/gate-prop-text.ts +145 -0
- package/src/reference/gate-prose-notation.ts +19 -5
- package/src/reference/gate-retired-keys.ts +101 -0
- package/src/reference/gloss.ts +2 -0
- package/src/reference/katex-measure.ts +384 -0
- package/src/reference/keyed-float-validate.ts +22 -0
- package/src/reference/keyed-jsx-floats.mjs +11 -14
- package/src/reference/lab-katex-validate.ts +156 -0
- package/src/reference/lab-math-scope.ts +15 -1
- package/src/reference/lint-ignore.ts +4 -1
- package/src/reference/math-glyphs.mjs +851 -46
- package/src/reference/mixed-content.ts +11 -2
- package/src/reference/page-notation.ts +114 -14
- package/src/reference/raw-floats.mjs +37 -13
- package/src/reference/references.ts +14 -0
- package/src/reference/registry.ts +170 -9
- package/src/reference/rehype-fail-katex-errors.mjs +19 -0
- package/src/reference/rehype-heading-anchors.mjs +11 -1
- package/src/reference/remark-citation.mjs +0 -0
- package/src/reference/remark-notation.mjs +275 -23
- package/src/reference/render-lab-math.ts +40 -5
- package/src/reference/sections-validate.ts +67 -3
- package/src/reference/sections.mjs +92 -17
- package/src/reference/tables-validate.ts +40 -2
- package/src/reference/text-measure.ts +63 -0
- package/src/reference/text-widths.ts +133 -0
- package/src/reference/types.ts +75 -8
- package/src/rules/catalog.ts +241 -31
- package/src/rules/settings.ts +10 -0
- package/src/session/PreferenceStore.ts +53 -0
- package/src/styles/global.css +322 -83
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, extname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { courseFileStem } from "./export-pages.js";
|
|
5
|
+
import {
|
|
6
|
+
consoleLogger,
|
|
7
|
+
diagramsOf,
|
|
8
|
+
figuresOf,
|
|
9
|
+
imageExtension,
|
|
10
|
+
imageFile,
|
|
11
|
+
imagesOf,
|
|
12
|
+
needsCapture,
|
|
13
|
+
prepareChapters,
|
|
14
|
+
readBuiltPage
|
|
15
|
+
} from "./export-chapters.js";
|
|
16
|
+
import { checkItems } from "./latex/document.js";
|
|
17
|
+
import { serializeMarkdownNodes, serializeMarkdownPage } from "./markdown/serialize.js";
|
|
18
|
+
import { zip } from "./zip.js";
|
|
19
|
+
async function pagePictures(outputRoot, base, page, captured, assetDir) {
|
|
20
|
+
const pictures = /* @__PURE__ */ new Map();
|
|
21
|
+
const copy = async (key, src, name) => {
|
|
22
|
+
const file = imageFile(outputRoot, base, page.route, src);
|
|
23
|
+
pictures.set(key, { path: `${assetDir}/${name}${extname(file).toLowerCase()}`, content: await readFile(file) });
|
|
24
|
+
};
|
|
25
|
+
for (const figure of figuresOf(page.blocks)) {
|
|
26
|
+
if (figure.picture.kind === "image" && (!needsCapture(figure) || imageExtension(figure.picture.src) === ".svg")) {
|
|
27
|
+
await copy(figure.anchor, figure.picture.src, figure.anchor);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const png = captured?.get(figure.anchor)?.png;
|
|
31
|
+
if (png) pictures.set(figure.anchor, { path: `${assetDir}/${figure.anchor}.png`, content: png });
|
|
32
|
+
}
|
|
33
|
+
for (const diagram of diagramsOf(page.blocks)) {
|
|
34
|
+
const png = captured?.get(diagram.anchor)?.png;
|
|
35
|
+
if (png) pictures.set(diagram.anchor, { path: `${assetDir}/${diagram.anchor}.png`, content: png });
|
|
36
|
+
}
|
|
37
|
+
for (const image of imagesOf(page.blocks)) {
|
|
38
|
+
if (pictures.has(image.src)) continue;
|
|
39
|
+
const name = basename(new URL(image.src, "https://x.invalid/").pathname, extname(image.src)) || "image";
|
|
40
|
+
await copy(image.src, image.src, name);
|
|
41
|
+
}
|
|
42
|
+
return pictures;
|
|
43
|
+
}
|
|
44
|
+
function twoDigits(value) {
|
|
45
|
+
return String(value).padStart(2, "0");
|
|
46
|
+
}
|
|
47
|
+
function chapterFile(lesson) {
|
|
48
|
+
return `${twoDigits(lesson.part)}-${twoDigits(lesson.chapter)}-${lesson.slug.split("/").at(-1) ?? lesson.slug}.md`;
|
|
49
|
+
}
|
|
50
|
+
function partFile(part) {
|
|
51
|
+
return `${twoDigits(part.ordinal)}-00-${part.dir}.md`;
|
|
52
|
+
}
|
|
53
|
+
function relativeTo(files, route) {
|
|
54
|
+
return new Map([...files].map(([key, file]) => [key, key === route ? "" : file]));
|
|
55
|
+
}
|
|
56
|
+
async function writeMarkdownExports(directory, manifest, options = {}) {
|
|
57
|
+
if (!manifest.lessonExports.markdown) return;
|
|
58
|
+
const logger = options.logger ?? consoleLogger;
|
|
59
|
+
const outputRoot = fileURLToPath(directory);
|
|
60
|
+
const { base, chapters, captures } = options.prepared ?? await prepareChapters(outputRoot, manifest, { logger, projectRoot: options.projectRoot });
|
|
61
|
+
const canonicalBaseUrl = manifest.lessonExports.canonicalBaseUrl;
|
|
62
|
+
const course = manifest.about.material.name;
|
|
63
|
+
const version = manifest.about.knowledge.version;
|
|
64
|
+
const source = (route) => ({
|
|
65
|
+
course,
|
|
66
|
+
version,
|
|
67
|
+
...canonicalBaseUrl ? { url: `${canonicalBaseUrl}${route ? `${route}/` : ""}` } : {}
|
|
68
|
+
});
|
|
69
|
+
const chapterFiles = async (chapter, file, assetDir, documentFiles) => {
|
|
70
|
+
const { lesson, page } = chapter;
|
|
71
|
+
const pictures = await pagePictures(outputRoot, base, page, captures.get(page.route), assetDir);
|
|
72
|
+
const markdown = serializeMarkdownPage(
|
|
73
|
+
{
|
|
74
|
+
page,
|
|
75
|
+
documentFiles: relativeTo(documentFiles, page.route),
|
|
76
|
+
...canonicalBaseUrl ? { canonicalBaseUrl } : {},
|
|
77
|
+
pictures: new Map([...pictures].map(([key, picture]) => [key, picture.path])),
|
|
78
|
+
checkItems: checkItems(manifest, lesson),
|
|
79
|
+
source: source(page.route)
|
|
80
|
+
},
|
|
81
|
+
{ number: `${lesson.part}.${lesson.chapter}` }
|
|
82
|
+
);
|
|
83
|
+
return [
|
|
84
|
+
{ path: file, content: markdown },
|
|
85
|
+
...[...pictures.values()].map((picture) => ({ path: picture.path, content: picture.content }))
|
|
86
|
+
];
|
|
87
|
+
};
|
|
88
|
+
const pending = [];
|
|
89
|
+
const pack = (target, folder, entries) => {
|
|
90
|
+
const unique = new Map(entries.map((entry) => [entry.path, entry]));
|
|
91
|
+
pending.push({
|
|
92
|
+
target: join(outputRoot, "downloads", target),
|
|
93
|
+
content: zip([...unique.values()].map((entry) => ({ path: `${folder}/${entry.path}`, content: entry.content })))
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
for (const chapter of chapters) {
|
|
97
|
+
const name = chapter.lesson.slug.split("/").at(-1) ?? chapter.lesson.slug;
|
|
98
|
+
pack(
|
|
99
|
+
`${chapter.lesson.slug}-markdown.zip`,
|
|
100
|
+
`${name}-markdown`,
|
|
101
|
+
await chapterFiles(chapter, "lesson.md", "assets", /* @__PURE__ */ new Map([[chapter.page.route, "lesson.md"]]))
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const byId = new Map(chapters.map((chapter) => [chapter.lesson.id, chapter]));
|
|
105
|
+
const pageSlugs = new Set(manifest.pages.map((page) => page.slug));
|
|
106
|
+
const parts = manifest.parts.map((part) => ({
|
|
107
|
+
part,
|
|
108
|
+
chapters: part.lessons.flatMap((id) => byId.has(id) ? [byId.get(id)] : []),
|
|
109
|
+
overview: pageSlugs.has(`${part.dir}/index`) ? `${part.dir}/index` : void 0
|
|
110
|
+
})).filter((entry) => entry.chapters.length > 0);
|
|
111
|
+
const overviewMarkdown = async (entry, documentFiles, assetDir) => {
|
|
112
|
+
if (!entry.overview) {
|
|
113
|
+
return { markdown: serializeMarkdownNodes([{ type: "heading", depth: 1, children: [{ type: "text", value: entry.part.label }] }]), pictures: [] };
|
|
114
|
+
}
|
|
115
|
+
const { page } = await readBuiltPage(outputRoot, entry.overview);
|
|
116
|
+
const pictures = await pagePictures(outputRoot, base, page, void 0, assetDir);
|
|
117
|
+
const markdown = serializeMarkdownPage(
|
|
118
|
+
{
|
|
119
|
+
page,
|
|
120
|
+
documentFiles: relativeTo(documentFiles, page.route),
|
|
121
|
+
...canonicalBaseUrl ? { canonicalBaseUrl } : {},
|
|
122
|
+
pictures: new Map([...pictures].map(([key, picture]) => [key, picture.path])),
|
|
123
|
+
checkItems: /* @__PURE__ */ new Map(),
|
|
124
|
+
source: source(page.route)
|
|
125
|
+
},
|
|
126
|
+
{ number: `Part ${entry.part.ordinal}:` }
|
|
127
|
+
);
|
|
128
|
+
return { markdown, pictures: [...pictures.values()].map((picture) => ({ path: picture.path, content: picture.content })) };
|
|
129
|
+
};
|
|
130
|
+
const contents = (entry, files) => entry.chapters.map((chapter) => ({
|
|
131
|
+
type: "listItem",
|
|
132
|
+
spread: false,
|
|
133
|
+
children: [
|
|
134
|
+
{
|
|
135
|
+
type: "paragraph",
|
|
136
|
+
children: [
|
|
137
|
+
{
|
|
138
|
+
type: "link",
|
|
139
|
+
url: files.get(chapter.page.route) ?? "",
|
|
140
|
+
children: [{ type: "text", value: `${chapter.lesson.part}.${chapter.lesson.chapter} ${chapter.page.title}` }]
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}));
|
|
146
|
+
for (const entry of parts) {
|
|
147
|
+
const files = new Map([
|
|
148
|
+
...entry.overview ? [[entry.part.dir, "index.md"]] : [],
|
|
149
|
+
...entry.chapters.map((chapter) => [chapter.page.route, chapterFile(chapter.lesson)])
|
|
150
|
+
]);
|
|
151
|
+
const overview = await overviewMarkdown(entry, files, "assets/index");
|
|
152
|
+
const index = `${overview.markdown}
|
|
153
|
+
` + serializeMarkdownNodes([
|
|
154
|
+
{ type: "heading", depth: 2, children: [{ type: "text", value: "Contents" }] },
|
|
155
|
+
{ type: "list", ordered: false, spread: false, children: contents(entry, files) }
|
|
156
|
+
]);
|
|
157
|
+
const entries = [{ path: "index.md", content: index }, ...overview.pictures];
|
|
158
|
+
for (const chapter of entry.chapters) {
|
|
159
|
+
const file = chapterFile(chapter.lesson);
|
|
160
|
+
entries.push(...await chapterFiles(chapter, file, `assets/${file.replace(/\.md$/u, "")}`, files));
|
|
161
|
+
}
|
|
162
|
+
pack(`part/${entry.part.dir}-markdown.zip`, `${entry.part.dir}-markdown`, entries);
|
|
163
|
+
}
|
|
164
|
+
if (parts.length > 0) {
|
|
165
|
+
const files = new Map([
|
|
166
|
+
["", "index.md"],
|
|
167
|
+
...parts.flatMap((entry) => [
|
|
168
|
+
...entry.overview ? [[entry.part.dir, partFile(entry.part)]] : [],
|
|
169
|
+
...entry.chapters.map((chapter) => [chapter.page.route, chapterFile(chapter.lesson)])
|
|
170
|
+
])
|
|
171
|
+
]);
|
|
172
|
+
const entries = [];
|
|
173
|
+
for (const entry of parts) {
|
|
174
|
+
if (entry.overview) {
|
|
175
|
+
const file = partFile(entry.part);
|
|
176
|
+
const overview = await overviewMarkdown(entry, files, `assets/${file.replace(/\.md$/u, "")}`);
|
|
177
|
+
entries.push({ path: file, content: overview.markdown }, ...overview.pictures);
|
|
178
|
+
}
|
|
179
|
+
for (const chapter of entry.chapters) {
|
|
180
|
+
const file = chapterFile(chapter.lesson);
|
|
181
|
+
entries.push(...await chapterFiles(chapter, file, `assets/${file.replace(/\.md$/u, "")}`, files));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const index = serializeMarkdownNodes([
|
|
185
|
+
{ type: "heading", depth: 1, children: [{ type: "text", value: course }] },
|
|
186
|
+
{
|
|
187
|
+
type: "paragraph",
|
|
188
|
+
children: [
|
|
189
|
+
{ type: "text", value: `Content version ${version}.` },
|
|
190
|
+
...canonicalBaseUrl ? [
|
|
191
|
+
{ type: "text", value: " Interactive version: " },
|
|
192
|
+
{ type: "link", url: canonicalBaseUrl, children: [{ type: "text", value: canonicalBaseUrl }] },
|
|
193
|
+
{ type: "text", value: "." }
|
|
194
|
+
] : []
|
|
195
|
+
]
|
|
196
|
+
},
|
|
197
|
+
...manifest.about.material.summary ? [{ type: "paragraph", children: [{ type: "text", value: manifest.about.material.summary }] }] : [],
|
|
198
|
+
{ type: "heading", depth: 2, children: [{ type: "text", value: "Contents" }] },
|
|
199
|
+
{
|
|
200
|
+
type: "list",
|
|
201
|
+
ordered: false,
|
|
202
|
+
spread: false,
|
|
203
|
+
children: parts.map((entry) => ({
|
|
204
|
+
type: "listItem",
|
|
205
|
+
spread: false,
|
|
206
|
+
children: [
|
|
207
|
+
{
|
|
208
|
+
type: "paragraph",
|
|
209
|
+
children: entry.overview ? [
|
|
210
|
+
{
|
|
211
|
+
type: "link",
|
|
212
|
+
url: partFile(entry.part),
|
|
213
|
+
children: [{ type: "text", value: `Part ${entry.part.ordinal}: ${entry.part.label}` }]
|
|
214
|
+
}
|
|
215
|
+
] : [{ type: "text", value: `Part ${entry.part.ordinal}: ${entry.part.label}` }]
|
|
216
|
+
},
|
|
217
|
+
{ type: "list", ordered: false, spread: false, children: contents(entry, files) }
|
|
218
|
+
]
|
|
219
|
+
}))
|
|
220
|
+
}
|
|
221
|
+
]);
|
|
222
|
+
entries.push({ path: "index.md", content: index });
|
|
223
|
+
pack("course-markdown.zip", `${courseFileStem(manifest)}-markdown`, entries);
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
for (const file of pending) {
|
|
227
|
+
await mkdir(dirname(file.target), { recursive: true });
|
|
228
|
+
await writeFile(`${file.target}.tmp-${process.pid}`, file.content);
|
|
229
|
+
}
|
|
230
|
+
for (const file of pending) await rename(`${file.target}.tmp-${process.pid}`, file.target);
|
|
231
|
+
} finally {
|
|
232
|
+
await Promise.all(pending.map((file) => rm(`${file.target}.tmp-${process.pid}`, { force: true })));
|
|
233
|
+
}
|
|
234
|
+
logger.info(
|
|
235
|
+
`Markdown export: ${chapters.length} chapter(s), ${parts.length} part(s)` + (parts.length > 0 ? " and the course" : "") + "."
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
export {
|
|
239
|
+
writeMarkdownExports
|
|
240
|
+
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Manifest } from '../manifest.js';
|
|
2
2
|
import '../../curriculum/validation.js';
|
|
3
3
|
import '../../reference/consistency.js';
|
|
4
|
-
import '../../course-config-
|
|
4
|
+
import '../../course-config-rRtME5UV.js';
|
|
5
5
|
import '../../rules/catalog.js';
|
|
6
6
|
import '../../rules/course-rules.js';
|
|
7
7
|
import '../collections.js';
|
|
8
8
|
import '../../reference/types.js';
|
|
9
|
+
import '../../reference/gate-retired-keys.js';
|
|
9
10
|
import '../../reference/lint-ignore.js';
|
|
10
11
|
import '../../reference/equations-validate.js';
|
|
11
12
|
import '../../reference/tables-validate.js';
|
|
@@ -16,6 +17,7 @@ import '../../reference/examples-validate.js';
|
|
|
16
17
|
import '../../reference/folds-validate.js';
|
|
17
18
|
import '../../reference/knowledge-checks-validate.js';
|
|
18
19
|
import '../../reference/raw-floats-validate.js';
|
|
20
|
+
import '../../reference/lab-katex-validate.js';
|
|
19
21
|
import '../../reference/gate-math.js';
|
|
20
22
|
import '../../reference/mixed-content.js';
|
|
21
23
|
import '../../reference/gloss.js';
|
|
@@ -23,17 +25,25 @@ import '../../rules/waivers.js';
|
|
|
23
25
|
import '../knowledge-version.js';
|
|
24
26
|
import '../lesson-derivation.js';
|
|
25
27
|
|
|
26
|
-
/** Inputs for producing one standalone
|
|
28
|
+
/** Inputs for producing one standalone page from Astro's final HTML. */
|
|
27
29
|
interface StandaloneLessonInput {
|
|
28
30
|
readonly html: string;
|
|
29
31
|
readonly sourceFile: string;
|
|
30
32
|
readonly outputRoot: string;
|
|
33
|
+
/** The page's docs slug: a chapter's, or `index`, `motion/index`, `glossary`. */
|
|
31
34
|
readonly lessonSlug: string;
|
|
32
35
|
readonly canonicalBaseUrl?: string;
|
|
33
36
|
}
|
|
34
|
-
/** Build one fully inlined
|
|
37
|
+
/** Build one fully inlined page document without writing it. */
|
|
35
38
|
declare function buildStandaloneLessonHtml(input: StandaloneLessonInput): Promise<string>;
|
|
36
|
-
/**
|
|
39
|
+
/** Astro's built HTML file of a docs slug. */
|
|
40
|
+
declare function pageSourceFile(outputRoot: string, slug: string): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Emit the opt-in standalone HTML artifact of every chapter
|
|
43
|
+
* (`standalonePageSlugs`) from Astro's completed static output, at
|
|
44
|
+
* `downloads/<docs slug>.html`. A page that is not a chapter is in its
|
|
45
|
+
* part's and the course's files (`combined-html.ts`).
|
|
46
|
+
*/
|
|
37
47
|
declare function writeStandaloneLessonExports(directory: URL, manifest: Manifest): Promise<void>;
|
|
38
48
|
|
|
39
|
-
export { type StandaloneLessonInput, buildStandaloneLessonHtml, writeStandaloneLessonExports };
|
|
49
|
+
export { type StandaloneLessonInput, buildStandaloneLessonHtml, pageSourceFile, writeStandaloneLessonExports };
|
|
@@ -9,10 +9,14 @@ import {
|
|
|
9
9
|
import { Buffer } from "node:buffer";
|
|
10
10
|
import { dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { parse, serialize } from "parse5";
|
|
13
|
+
import { exportPageRoute, standalonePageSlugs } from "./export-pages.js";
|
|
12
14
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
attribute,
|
|
16
|
+
elements,
|
|
17
|
+
hasClass,
|
|
18
|
+
inferBasePrefix
|
|
19
|
+
} from "./html-tree.js";
|
|
16
20
|
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
|
17
21
|
const CSS_URL = /url\(\s*(?:(["'])(.*?)\1|([^)]*?))\s*\)/giu;
|
|
18
22
|
const CSS_IMPORT = /@import\s+(?:url\(\s*(?:(["'])(.*?)\1|([^)]*?))\s*\)|(["'])(.*?)\4)\s*([^;]*);/giu;
|
|
@@ -98,38 +102,11 @@ const STANDALONE_THEME_SCRIPT = `(() => {
|
|
|
98
102
|
});
|
|
99
103
|
apply('auto');
|
|
100
104
|
})();`;
|
|
101
|
-
function isElement(node) {
|
|
102
|
-
return "tagName" in node;
|
|
103
|
-
}
|
|
104
|
-
function childrenOf(node) {
|
|
105
|
-
return "childNodes" in node ? node.childNodes : [];
|
|
106
|
-
}
|
|
107
|
-
function walk(node, visit) {
|
|
108
|
-
visit(node);
|
|
109
|
-
for (const child of childrenOf(node)) walk(child, visit);
|
|
110
|
-
if (isElement(node) && node.tagName === "template") {
|
|
111
|
-
const template = node;
|
|
112
|
-
for (const child of template.content.childNodes) walk(child, visit);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function elements(root, predicate) {
|
|
116
|
-
const found = [];
|
|
117
|
-
walk(root, (node) => {
|
|
118
|
-
if (isElement(node) && (predicate?.(node) ?? true)) found.push(node);
|
|
119
|
-
});
|
|
120
|
-
return found;
|
|
121
|
-
}
|
|
122
105
|
function firstElement(root, tagName) {
|
|
123
106
|
const found = elements(root, (node) => node.tagName === tagName)[0];
|
|
124
107
|
if (!found) throw new Error(`Standalone HTML source has no <${tagName}> element.`);
|
|
125
108
|
return found;
|
|
126
109
|
}
|
|
127
|
-
function attribute(node, name) {
|
|
128
|
-
return node.attrs.find((entry) => entry.name === name)?.value;
|
|
129
|
-
}
|
|
130
|
-
function hasClass(node, name) {
|
|
131
|
-
return (attribute(node, "class") ?? "").split(/\s+/u).includes(name);
|
|
132
|
-
}
|
|
133
110
|
function setAttribute(node, name, value = "") {
|
|
134
111
|
const existing = node.attrs.find((entry) => entry.name === name);
|
|
135
112
|
if (existing) existing.value = value;
|
|
@@ -392,7 +369,7 @@ async function bundleBrowserGraph(entryPaths, requests, context) {
|
|
|
392
369
|
});
|
|
393
370
|
const javascript = result.outputFiles.find((file) => file.path.endsWith(".js"));
|
|
394
371
|
if (!javascript) {
|
|
395
|
-
throw new Error("Browser bundler produced no JavaScript for the standalone
|
|
372
|
+
throw new Error("Browser bundler produced no JavaScript for the standalone page.");
|
|
396
373
|
}
|
|
397
374
|
const unsupported = result.outputFiles.filter(
|
|
398
375
|
(file) => !file.path.endsWith(".js") && !file.path.endsWith(".css")
|
|
@@ -408,17 +385,6 @@ async function bundleBrowserGraph(entryPaths, requests, context) {
|
|
|
408
385
|
css: result.outputFiles.filter((file) => file.path.endsWith(".css")).map((file) => file.text)
|
|
409
386
|
};
|
|
410
387
|
}
|
|
411
|
-
function inferBasePrefix(document) {
|
|
412
|
-
for (const node of elements(document)) {
|
|
413
|
-
for (const name of ["href", "src", "component-url", "renderer-url"]) {
|
|
414
|
-
const value = attribute(node, name);
|
|
415
|
-
if (!value?.startsWith("/")) continue;
|
|
416
|
-
const marker = value.indexOf("/astro-assets/");
|
|
417
|
-
if (marker !== -1) return `${value.slice(0, marker)}/`;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
return "/";
|
|
421
|
-
}
|
|
422
388
|
function normalizedPagePath(pathname) {
|
|
423
389
|
return pathname.replace(/\/index\.html$/iu, "/").replace(/\.html$/iu, "").replace(/\/+$/u, "");
|
|
424
390
|
}
|
|
@@ -429,14 +395,17 @@ function rewriteCourseLink(href, lessonSlug, basePrefix, canonicalBaseUrl) {
|
|
|
429
395
|
if (hasScheme(trimmed)) {
|
|
430
396
|
throw new Error(`Standalone HTML cannot publish local navigation URL ${JSON.stringify(href)}.`);
|
|
431
397
|
}
|
|
432
|
-
const pagePath = `${basePrefix}${lessonSlug}/`.replace(
|
|
398
|
+
const pagePath = `${basePrefix}${exportPageRoute(lessonSlug)}/`.replace(
|
|
399
|
+
/\/{2,}/gu,
|
|
400
|
+
"/"
|
|
401
|
+
);
|
|
433
402
|
const resolved = new URL(trimmed, `https://explico.invalid${pagePath}`);
|
|
434
403
|
if (normalizedPagePath(resolved.pathname) === normalizedPagePath(pagePath)) {
|
|
435
404
|
return `${resolved.search}${resolved.hash}` || "#_top";
|
|
436
405
|
}
|
|
437
406
|
if (!canonicalBaseUrl) {
|
|
438
407
|
throw new Error(
|
|
439
|
-
`
|
|
408
|
+
`Page ${lessonSlug} links outside itself (${JSON.stringify(href)}), so \`lessonExports.canonicalBaseUrl\` is required for standalone HTML.`
|
|
440
409
|
);
|
|
441
410
|
}
|
|
442
411
|
let coursePath = resolved.pathname;
|
|
@@ -751,7 +720,7 @@ async function buildStandaloneLessonHtml(input) {
|
|
|
751
720
|
const outputRoot = resolve(input.outputRoot);
|
|
752
721
|
const sourceFile = resolve(input.sourceFile);
|
|
753
722
|
if (!insideRoot(sourceFile, outputRoot)) {
|
|
754
|
-
throw new Error("Standalone
|
|
723
|
+
throw new Error("Standalone page source must be inside Astro build output.");
|
|
755
724
|
}
|
|
756
725
|
const context = {
|
|
757
726
|
outputRoot,
|
|
@@ -778,8 +747,10 @@ async function buildStandaloneLessonHtml(input) {
|
|
|
778
747
|
auditStandaloneHtml(html, input.lessonSlug);
|
|
779
748
|
return html;
|
|
780
749
|
}
|
|
781
|
-
async function
|
|
782
|
-
const
|
|
750
|
+
async function pageSourceFile(outputRoot, slug) {
|
|
751
|
+
const route = exportPageRoute(slug);
|
|
752
|
+
const candidates = [join(outputRoot, route, "index.html")];
|
|
753
|
+
if (route) candidates.push(join(outputRoot, `${route}.html`));
|
|
783
754
|
for (const candidate of candidates) if (await isFile(candidate)) return candidate;
|
|
784
755
|
throw new Error(
|
|
785
756
|
`Standalone HTML could not find Astro's built page for ${slug}; tried ` + candidates.map((path) => relative(outputRoot, path)).join(", ")
|
|
@@ -789,9 +760,9 @@ async function writeStandaloneLessonExports(directory, manifest) {
|
|
|
789
760
|
if (!manifest.lessonExports.standaloneHtml) return;
|
|
790
761
|
const outputRoot = fileURLToPath(directory);
|
|
791
762
|
const pending = [];
|
|
792
|
-
for (const
|
|
793
|
-
const sourceFile = await
|
|
794
|
-
const target = join(outputRoot, "downloads", `${
|
|
763
|
+
for (const slug of standalonePageSlugs(manifest)) {
|
|
764
|
+
const sourceFile = await pageSourceFile(outputRoot, slug);
|
|
765
|
+
const target = join(outputRoot, "downloads", `${slug}.html`);
|
|
795
766
|
pending.push({
|
|
796
767
|
target,
|
|
797
768
|
temporary: `${target}.tmp-${process.pid}`,
|
|
@@ -799,7 +770,7 @@ async function writeStandaloneLessonExports(directory, manifest) {
|
|
|
799
770
|
html: await readFile(sourceFile, "utf8"),
|
|
800
771
|
sourceFile,
|
|
801
772
|
outputRoot,
|
|
802
|
-
lessonSlug:
|
|
773
|
+
lessonSlug: slug,
|
|
803
774
|
canonicalBaseUrl: manifest.lessonExports.canonicalBaseUrl
|
|
804
775
|
})
|
|
805
776
|
});
|
|
@@ -812,5 +783,6 @@ async function writeStandaloneLessonExports(directory, manifest) {
|
|
|
812
783
|
}
|
|
813
784
|
export {
|
|
814
785
|
buildStandaloneLessonHtml,
|
|
786
|
+
pageSourceFile,
|
|
815
787
|
writeStandaloneLessonExports
|
|
816
788
|
};
|