Skip to content

Commit

Permalink
add djot
Browse files Browse the repository at this point in the history
  • Loading branch information
matklad committed Nov 12, 2022
1 parent 2d99e25 commit 58b876f
Show file tree
Hide file tree
Showing 105 changed files with 19,729 additions and 3 deletions.
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/vendor
/_site
.sass-cache
.jekyll-cache
/tmp
/djot
90 changes: 90 additions & 0 deletions adoc2djot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env -S deno run --allow-write --allow-read

/*
TODO:
- tables (pipes, style)
- youtube video
- "`quotes`"
- footnotes (unsafe is a human-assited type system)
- stripes in highligted lines
- emdashes (`\w -- \w`)
- `++`
*/

const path = Deno.args[0];
let s = await Deno.readTextFile(path);
s = s.replace(":page-liquid:\n", "");
s = s.replaceAll("{cpp}", "C++");
s = s.replace(/^(=+ )/mg, (m) => {
return "#".repeat(m.length - 1) + " ";
});
s = s.replace(/(http\S*?)(\[.*?(\n.*?)?\])/mg, (m, link, text) => {
return `${text}(${link})`;
});
s = s.replace(/^:(\w*?): (.*?)$/mg, (m, ref, link) => {
return `[${ref}]: ${link}`;
});
s = s.replace(/\{(\w*?)\}\[(.*?)\]/mg, (m, ref, text) => {
return `[${text}][${ref}]`;
});
s = s.replace(/^\[source\]\n----\n((.|\n)*?)\n----/mg, (m, code) => {
return "```\n" + code + "\n```";
});
s = s.replace(
/^\[source,(kotlin|cpp|rust|swift|c|toml|go|console|js|ts|nix)(,highlight=.*?)?\]\n----\n((.|\n)*?)\n----/mg,
(m, lang, hl, code) => {
let highlight = ""
if (hl) {
highlight = hl.substring(",highlight=".length).replaceAll('..', '-').replaceAll(';', ',').replaceAll('"', '')
highlight = `{highlight="${highlight}"}\n`;
}
return highlight + "```" + lang + "\n" + code + "\n```";
},
);
s = s.replace(
/^\[source(,highlight=.*?)\]\n----\n((.|\n)*?)\n----/mg,
(m, hl, code) => {
let highlight = ""
if (hl) {
highlight = hl.substring(",highlight=".length).replaceAll('..', '-').replaceAll(';', ',').replaceAll('"', '')
highlight = `{highlight="${highlight}"}\n`;
}
return highlight + "```\n" + code + "\n```";
},
);
s = s.replace(/^\.(.*?)$\n```/mg, (m, title) => {
return `{cap="${title}"}` + "\n```";
});
s = s.replace(/^image::(.*)\[\]$/mg, (m, path) => {
return `![](${path})`;
});
s = s.replace(/kbd:\[(.*?)\]/mg, (m, text) => {
return `[${text}]{.kbd}`;
});
s = s.replace(/(\n\. .*?$(\n\s\s.*?$)*){2,}/mg, (m, text) => {
let i = 0;
return m.replace(/^\. /mg, (m) => {
i += 1;
return `${i}. `;
});
});
s = s.replace(/\[(NOTE|Note)\]\n====\n((.|\n)*?)\n====\n/mg, (m, note, content) => {
return `::: note\n${content}\n:::\n`
})
s = s.replace(/^NOTE: (.*?)$/mg, (m, content) => {
return `::: note\n${content}\n:::\n`
})
s = s.replace(/^\*\*\*\*\n((.|\n)*?)\n\*\*\*\*\n/mg, (m, content) => {
return `::: snip\n${content}\n:::\n`
})
s = s.replace(/"`(\w.*?(\w|\?))`"/mg, (m, w) => {
return `"${w}"`
})
s = s.replace(/^(http(s)?:\/\/.*)$/mg, (m) => {
return `<${m}>`
})
s = s.replace(/`\+(.*?)\+`/mg, (m, w) => {
return "`" + w + "`"
})

await Deno.writeTextFile(path, s);
151 changes: 151 additions & 0 deletions build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/env -S deno run --allow-write=./_site,./tmp --allow-read=/tmp,./ --allow-net --allow-run=./main.ts,lua
import { std } from "./deps.ts";
import * as templates from "./templates.ts";
import * as djot from "./djot.ts";
import { HtmlString } from "./templates.ts";

let build_id = 0;
async function watch() {
async function rebuild() {
try {
console.log(`rebuild #${build_id}`);
build_id += 1;
await Deno.run({ cmd: ["./main.ts", "build", "--update"] }).status();
} catch {
// ignore
}
}

await std.fs.emptyDir("./_site");
await rebuild();

const rebuild_debounced = std.async.debounce(
rebuild,
16,
);

outer:
for await (const event of Deno.watchFs("./", { recursive: true })) {
for (const path of event.paths) {
if (path.match(/\.\/(tmp|_site)/)) {
continue outer;
}
}
if (event.kind == "access") continue outer;
rebuild_debounced();
}
}

async function build() {
const start = performance.now();

if (Deno.args.includes("--update")) {
await Deno.mkdir("_site", { recursive: true });
} else {
await std.fs.emptyDir("./_site");
}

const posts = await collect_posts();

await update_file("_site/index.html", templates.post_list(posts).value);
await update_file("_site/feed.xml", templates.feed(posts).value);
await update_file("_site/about.html", templates.about().value);
for (const post of posts) {
await update_file(`_site${post.path}`, templates.post(post).value);
}

const paths = [
"favicon.ico",
"css/*",
"assets/*",
];
for (const path of paths) {
await update_path(path);
}

const end = performance.now();
console.log(`${end - start}ms`);
}

async function update_file(path: string, contents: Uint8Array | string) {
if (!contents) return;
await std.fs.ensureFile(path);
await std.fs.ensureDir("./tmp");
const temp = await Deno.makeTempFile({ dir: "./tmp" });
if (contents instanceof Uint8Array) {
await Deno.writeFile(temp, contents);
} else {
await Deno.writeTextFile(temp, contents);
}
await Deno.rename(temp, path);
}

async function update_path(path: string) {
if (path.endsWith("*")) {
const dir = path.replace("*", "");
const futs = [];
for await (const entry of Deno.readDir(`src/${dir}`)) {
futs.push(update_path(`${dir}/${entry.name}`));
}
await Promise.all(futs);
} else {
await update_file(
`_site/${path}`,
await Deno.readFile(`src/${path}`),
);
}
}

export type Post = {
year: number;
month: number;
day: number;
slug: string;
date: Date;
title: HtmlString;
path: string;
src: string;
content: HtmlString;
summary: HtmlString;
};

async function collect_posts(): Promise<Post[]> {
const post_walk = std.fs.walk("./src/posts", { includeDirs: false });
const work = std.async.pooledMap(8, post_walk, async (entry) => {
if (!entry.name.endsWith(".djot")) return undefined;
const [, y, m, d, slug] = entry.name.match(
/^(\d\d\d\d)-(\d\d)-(\d\d)-(.*)\.djot$/,
)!;
const [year, month, day] = [y, m, d].map((it) => parseInt(it, 10));
const date = new Date(Date.UTC(year, month - 1, day));

const text = await Deno.readTextFile(entry.path);
const ast = await djot.parse(text);
const ctx = { date };
const html = djot.render(ast, ctx);

const title = ast.child("heading")?.content ?? "untitled";
return {
year,
month,
day,
slug,
date,
title,
content: html,
summary: ctx.summary,
path: `/${y}/${m}/${d}/${slug}.html`,
src: `/src/posts/${y}-${m}-${d}-${slug}.djot`,
};
});

const posts = [];
for await (const it of work) if (it) posts.push(it);
posts.sort((l, r) => l.path < r.path ? 1 : -1);
return posts;
}

export const commands: { [key: string]: () => Promise<void> } = {
watch,
build,
};
23 changes: 23 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as async from "https://deno.land/[email protected]/async/mod.ts";
import * as fs from "https://deno.land/[email protected]/fs/mod.ts";
import * as path from "https://deno.land/[email protected]/path/mod.ts";
import * as streams from "https://deno.land/[email protected]/streams/mod.ts";

export const std = {
async,
fs,
path,
streams,
};

import hljs_ from "https://unpkg.com/@highlightjs/[email protected]/es/highlight.min.js";
import latex from "https://unpkg.com/@highlightjs/[email protected]/es/languages/latex.min.js";
import nix from "https://unpkg.com/@highlightjs/[email protected]/es/languages/nix.min.js";
import x86asm from "https://unpkg.com/@highlightjs/[email protected]/es/languages/x86asm.min.js";
let hljs: any = hljs_
hljs.configure({ classPrefix: "hl-" });
hljs.registerLanguage("latex", latex);
hljs.registerLanguage("nix", nix);
hljs.registerLanguage("x86asm", x86asm);

export { hljs };
Loading

0 comments on commit 58b876f

Please sign in to comment.