Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A deno script to make a single plain HTML file version of the deno manual

Note that this is a very quick thing. In particular, it only handles two levels of documentation in the manual, and it doesn't include images. Both would need to be fixed for this to actually be useful; however, this was entirely sufficient to drop a copy onto my phone where I could read it while my stupid mobile operator pretends to fix my network, sigh, etc.

/* run as
deno run --allow-read --allow-write=deno-manual.html make-manual.js
*/
import { Marked } from "https://deno.land/x/markdown/mod.ts";
const load_file_to_html = async fn => {
const pfn = new URL(`deno/docs/${fn}.md`, import.meta.url).pathname
console.log("Loading", pfn)
const decoder = new TextDecoder("utf-8");
let filedata
try {
filedata = await Deno.readFile(pfn)
} catch(e) {
if (e.name == "NotFound") {
console.error(`Couldn't find the Deno docs.
I looked for file '${pfn}' and couldn't find it.
You need a Deno checkout available; try
git clone --depth=1 git@github.com:denoland/deno.git
`)
Deno.exit(1);
}
throw e;
}
const markdown = decoder.decode(filedata);
return Marked.parse(markdown);
}
const load_files_to_html = async (toc) => {
/* Load each of the files in the TOC as Markdown, convert to HTML, and stash */
const loaded = {};
let parent_id = 0;
for (let [parent, details] of Object.entries(toc)) {
parent_id += 1;
loaded[parent] = {
html: await load_file_to_html(parent),
title: details.name,
index: parent_id.toString()
}
let child_id = 0;
for (let [child, title] of Object.entries(details.children || {})) {
child_id += 1;
loaded[child] = {
html: await load_file_to_html(`${parent}/${child}`),
title: title,
index: `${parent_id}.${child_id}`
}
}
}
return loaded;
}
const convert_toc_to_html = (toc, all_html) => {
const outlist = []
for (let [pagekey, details] of Object.entries(all_html)) {
outlist.push(`<li id="toc_${pagekey}">${details.index}. <a href="#${pagekey}">${details.title}</a></li>`);
}
return "<ul>" + outlist.join("\n") + "</ul>";
}
const make_single_file = (toc_html, all_html) => {
let outhtml = [`<!doctype html><html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html {
font-family: "Inter var",system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
}
</style>
<link rel="stylesheet" href="pygments.css">
</head><body>`];
outhtml.push("<h1>Deno Manual</h1>")
outhtml.push(toc_html)
for (let [pagekey, details] of Object.entries(all_html)) {
let is_child = details.index.indexOf(".") > -1;
let heading_level = is_child ? 3 : 2;
outhtml.push("<article>")
const line = `<h${heading_level} id="${pagekey}"><a href="#toc_${pagekey}">${details.index}</a>. ${details.title}</h${heading_level}>`;
outhtml.push(line)
outhtml.push(details["html"])
outhtml.push("</article>")
}
return outhtml.join("\n");
}
const main = async () => {
/* Note that this assumes you've already got a checkout of deno.
If you haven't, git clone --depth=1 git@github.com:denoland/deno.git */
/*
Read the json file for the table of contents: this both defines
the table of contents itself so we can turn it into HTML, and also
defines the list of files that need converting.
*/
const decoder = new TextDecoder("utf-8");
const toc = JSON.parse(decoder.decode(await Deno.readFile("deno/docs/toc.json")));
/* We load all the files into one big dictionary of HTML, based on the TOC */
const all_html = await load_files_to_html(toc);
/* Create the table of contents itself */
const toc_html = convert_toc_to_html(toc, all_html)
/* Produce the output as HTML */
const output_html = make_single_file(toc_html, all_html)
/* and write it out */
const encoder = new TextEncoder();
const data = encoder.encode(output_html);
await Deno.writeFile("deno-manual.html", data);
}
main().then(() => { console.log("done"); });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment