Skip to content

Commit 58b876f

Browse files
committed
add djot
1 parent 2d99e25 commit 58b876f

File tree

105 files changed

+19729
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+19729
-3
lines changed

.gitignore

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/vendor
21
/_site
3-
.sass-cache
4-
.jekyll-cache
2+
/tmp
3+
/djot

adoc2djot.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env -S deno run --allow-write --allow-read
2+
3+
/*
4+
TODO:
5+
- tables (pipes, style)
6+
- youtube video
7+
- "`quotes`"
8+
- footnotes (unsafe is a human-assited type system)
9+
- stripes in highligted lines
10+
- emdashes (`\w -- \w`)
11+
- `++`
12+
*/
13+
14+
const path = Deno.args[0];
15+
let s = await Deno.readTextFile(path);
16+
s = s.replace(":page-liquid:\n", "");
17+
s = s.replaceAll("{cpp}", "C++");
18+
s = s.replace(/^(=+ )/mg, (m) => {
19+
return "#".repeat(m.length - 1) + " ";
20+
});
21+
s = s.replace(/(http\S*?)(\[.*?(\n.*?)?\])/mg, (m, link, text) => {
22+
return `${text}(${link})`;
23+
});
24+
s = s.replace(/^:(\w*?): (.*?)$/mg, (m, ref, link) => {
25+
return `[${ref}]: ${link}`;
26+
});
27+
s = s.replace(/\{(\w*?)\}\[(.*?)\]/mg, (m, ref, text) => {
28+
return `[${text}][${ref}]`;
29+
});
30+
s = s.replace(/^\[source\]\n----\n((.|\n)*?)\n----/mg, (m, code) => {
31+
return "```\n" + code + "\n```";
32+
});
33+
s = s.replace(
34+
/^\[source,(kotlin|cpp|rust|swift|c|toml|go|console|js|ts|nix)(,highlight=.*?)?\]\n----\n((.|\n)*?)\n----/mg,
35+
(m, lang, hl, code) => {
36+
let highlight = ""
37+
if (hl) {
38+
highlight = hl.substring(",highlight=".length).replaceAll('..', '-').replaceAll(';', ',').replaceAll('"', '')
39+
highlight = `{highlight="${highlight}"}\n`;
40+
}
41+
return highlight + "```" + lang + "\n" + code + "\n```";
42+
},
43+
);
44+
s = s.replace(
45+
/^\[source(,highlight=.*?)\]\n----\n((.|\n)*?)\n----/mg,
46+
(m, hl, code) => {
47+
let highlight = ""
48+
if (hl) {
49+
highlight = hl.substring(",highlight=".length).replaceAll('..', '-').replaceAll(';', ',').replaceAll('"', '')
50+
highlight = `{highlight="${highlight}"}\n`;
51+
}
52+
return highlight + "```\n" + code + "\n```";
53+
},
54+
);
55+
s = s.replace(/^\.(.*?)$\n```/mg, (m, title) => {
56+
return `{cap="${title}"}` + "\n```";
57+
});
58+
s = s.replace(/^image::(.*)\[\]$/mg, (m, path) => {
59+
return `![](${path})`;
60+
});
61+
s = s.replace(/kbd:\[(.*?)\]/mg, (m, text) => {
62+
return `[${text}]{.kbd}`;
63+
});
64+
s = s.replace(/(\n\. .*?$(\n\s\s.*?$)*){2,}/mg, (m, text) => {
65+
let i = 0;
66+
return m.replace(/^\. /mg, (m) => {
67+
i += 1;
68+
return `${i}. `;
69+
});
70+
});
71+
s = s.replace(/\[(NOTE|Note)\]\n====\n((.|\n)*?)\n====\n/mg, (m, note, content) => {
72+
return `::: note\n${content}\n:::\n`
73+
})
74+
s = s.replace(/^NOTE: (.*?)$/mg, (m, content) => {
75+
return `::: note\n${content}\n:::\n`
76+
})
77+
s = s.replace(/^\*\*\*\*\n((.|\n)*?)\n\*\*\*\*\n/mg, (m, content) => {
78+
return `::: snip\n${content}\n:::\n`
79+
})
80+
s = s.replace(/"`(\w.*?(\w|\?))`"/mg, (m, w) => {
81+
return `"${w}"`
82+
})
83+
s = s.replace(/^(http(s)?:\/\/.*)$/mg, (m) => {
84+
return `<${m}>`
85+
})
86+
s = s.replace(/`\+(.*?)\+`/mg, (m, w) => {
87+
return "`" + w + "`"
88+
})
89+
90+
await Deno.writeTextFile(path, s);

build.ts

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/env -S deno run --allow-write=./_site,./tmp --allow-read=/tmp,./ --allow-net --allow-run=./main.ts,lua
2+
import { std } from "./deps.ts";
3+
import * as templates from "./templates.ts";
4+
import * as djot from "./djot.ts";
5+
import { HtmlString } from "./templates.ts";
6+
7+
let build_id = 0;
8+
async function watch() {
9+
async function rebuild() {
10+
try {
11+
console.log(`rebuild #${build_id}`);
12+
build_id += 1;
13+
await Deno.run({ cmd: ["./main.ts", "build", "--update"] }).status();
14+
} catch {
15+
// ignore
16+
}
17+
}
18+
19+
await std.fs.emptyDir("./_site");
20+
await rebuild();
21+
22+
const rebuild_debounced = std.async.debounce(
23+
rebuild,
24+
16,
25+
);
26+
27+
outer:
28+
for await (const event of Deno.watchFs("./", { recursive: true })) {
29+
for (const path of event.paths) {
30+
if (path.match(/\.\/(tmp|_site)/)) {
31+
continue outer;
32+
}
33+
}
34+
if (event.kind == "access") continue outer;
35+
rebuild_debounced();
36+
}
37+
}
38+
39+
async function build() {
40+
const start = performance.now();
41+
42+
if (Deno.args.includes("--update")) {
43+
await Deno.mkdir("_site", { recursive: true });
44+
} else {
45+
await std.fs.emptyDir("./_site");
46+
}
47+
48+
const posts = await collect_posts();
49+
50+
await update_file("_site/index.html", templates.post_list(posts).value);
51+
await update_file("_site/feed.xml", templates.feed(posts).value);
52+
await update_file("_site/about.html", templates.about().value);
53+
for (const post of posts) {
54+
await update_file(`_site${post.path}`, templates.post(post).value);
55+
}
56+
57+
const paths = [
58+
"favicon.ico",
59+
"css/*",
60+
"assets/*",
61+
];
62+
for (const path of paths) {
63+
await update_path(path);
64+
}
65+
66+
const end = performance.now();
67+
console.log(`${end - start}ms`);
68+
}
69+
70+
async function update_file(path: string, contents: Uint8Array | string) {
71+
if (!contents) return;
72+
await std.fs.ensureFile(path);
73+
await std.fs.ensureDir("./tmp");
74+
const temp = await Deno.makeTempFile({ dir: "./tmp" });
75+
if (contents instanceof Uint8Array) {
76+
await Deno.writeFile(temp, contents);
77+
} else {
78+
await Deno.writeTextFile(temp, contents);
79+
}
80+
await Deno.rename(temp, path);
81+
}
82+
83+
async function update_path(path: string) {
84+
if (path.endsWith("*")) {
85+
const dir = path.replace("*", "");
86+
const futs = [];
87+
for await (const entry of Deno.readDir(`src/${dir}`)) {
88+
futs.push(update_path(`${dir}/${entry.name}`));
89+
}
90+
await Promise.all(futs);
91+
} else {
92+
await update_file(
93+
`_site/${path}`,
94+
await Deno.readFile(`src/${path}`),
95+
);
96+
}
97+
}
98+
99+
export type Post = {
100+
year: number;
101+
month: number;
102+
day: number;
103+
slug: string;
104+
date: Date;
105+
title: HtmlString;
106+
path: string;
107+
src: string;
108+
content: HtmlString;
109+
summary: HtmlString;
110+
};
111+
112+
async function collect_posts(): Promise<Post[]> {
113+
const post_walk = std.fs.walk("./src/posts", { includeDirs: false });
114+
const work = std.async.pooledMap(8, post_walk, async (entry) => {
115+
if (!entry.name.endsWith(".djot")) return undefined;
116+
const [, y, m, d, slug] = entry.name.match(
117+
/^(\d\d\d\d)-(\d\d)-(\d\d)-(.*)\.djot$/,
118+
)!;
119+
const [year, month, day] = [y, m, d].map((it) => parseInt(it, 10));
120+
const date = new Date(Date.UTC(year, month - 1, day));
121+
122+
const text = await Deno.readTextFile(entry.path);
123+
const ast = await djot.parse(text);
124+
const ctx = { date };
125+
const html = djot.render(ast, ctx);
126+
127+
const title = ast.child("heading")?.content ?? "untitled";
128+
return {
129+
year,
130+
month,
131+
day,
132+
slug,
133+
date,
134+
title,
135+
content: html,
136+
summary: ctx.summary,
137+
path: `/${y}/${m}/${d}/${slug}.html`,
138+
src: `/src/posts/${y}-${m}-${d}-${slug}.djot`,
139+
};
140+
});
141+
142+
const posts = [];
143+
for await (const it of work) if (it) posts.push(it);
144+
posts.sort((l, r) => l.path < r.path ? 1 : -1);
145+
return posts;
146+
}
147+
148+
export const commands: { [key: string]: () => Promise<void> } = {
149+
watch,
150+
build,
151+
};

deps.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as async from "https://deno.land/std@0.161.0/async/mod.ts";
2+
import * as fs from "https://deno.land/std@0.161.0/fs/mod.ts";
3+
import * as path from "https://deno.land/std@0.161.0/path/mod.ts";
4+
import * as streams from "https://deno.land/std@0.161.0/streams/mod.ts";
5+
6+
export const std = {
7+
async,
8+
fs,
9+
path,
10+
streams,
11+
};
12+
13+
import hljs_ from "https://unpkg.com/@highlightjs/cdn-assets@11.6.0/es/highlight.min.js";
14+
import latex from "https://unpkg.com/@highlightjs/cdn-assets@11.6.0/es/languages/latex.min.js";
15+
import nix from "https://unpkg.com/@highlightjs/cdn-assets@11.6.0/es/languages/nix.min.js";
16+
import x86asm from "https://unpkg.com/@highlightjs/cdn-assets@11.6.0/es/languages/x86asm.min.js";
17+
let hljs: any = hljs_
18+
hljs.configure({ classPrefix: "hl-" });
19+
hljs.registerLanguage("latex", latex);
20+
hljs.registerLanguage("nix", nix);
21+
hljs.registerLanguage("x86asm", x86asm);
22+
23+
export { hljs };

0 commit comments

Comments
 (0)