1// supervise wraps the entire process in order to allow @clo/lib/incremental to
2// restart the entire process if this file or node_modules changes, all without
3// losing incremental state where it matters.
4await incremental.supervise(async () => {
5 const graph = new incremental.Graph({
6 db: ".clover/incremental-markdown.db",
7 root: import.meta.dirname,
8 });
9
10 for await (const result of graph.incrementalWatch({ buildAndWriteOutput, progress })) {
11 // errors stay listed until they are fixed, even when cached
12 if (!result.ok) console.error(result.error);
13 log.info(result.statusLine);
14 }
15});
16
17async function buildAndWriteOutput(ref: incremental.Ref) {
18 using incr = ref.start("build markdown blog"); // see @clo/lib/progress
19
20 // First, place a dependency on the contents of `src/posts`.
21 const files = await incr.step(async (b) => (await b.readDir("posts")).filter(x => x.endsWith(".md"))).read();
22
23 // Then, build every page as it's own step.
24 const pages = await Promise.all(files.map(async (file) => {
25 const { text, title } = await incr.step(renderPage, `posts/${file}`).read();
26 const base = file.replace(/\.md$/, "");
27 await incr.writeFile(`out/${base}.html`, text);
28 return { title, url: `${base}.html` };
29 }));
30
31 // Then, build the homepage and write it out.
32 const index = incr.step(renderHomepage, pages);
33 await incr.writeFile("out/index.html", index);
34}
35
36async function renderPage(
37 b: incremental.Build,
38 file: string,
39) {
40 using _ = b.start(`Render ${file}`);
41 log.info(`renderPage(${file})`);
42
43 await async.delay(500 + Math.random() * 500);
44
45 // Place a dependency on the markdown library. Similarly, this could be
46 // importing the page itself if it was code.
47 const lib = await b.import<typeof import("./formatter.ts")>("formatter.ts");
48 // Place a dependency on the page's file contents.
49 const source = await b.readFile(file).text();
50
51 return lib.formatPage(source);
52}
53
54async function renderHomepage(
55 b: incremental.Build,
56 pages: Array<Page>,
57) {
58 using _ = b.start(`Build homepage`);
59
60 const lib = await b.import<typeof import("./formatter.ts")>("formatter.ts");
61 const { text } = await renderPage(b, "index.md");
62 return text.replace("%index%", lib.formatList(pages));
63}
64
65import * as async from "@clo/lib/async";
66import * as incremental from "@clo/lib/incremental";
67import * as log from "@clo/lib/log";
68import * as progress from "@clo/lib/progress";
69import type { Page } from "./formatter.ts";