const { readdir, readFile, writeFile } = require("fs").promises;
const fetch = require("node-fetch");

const forEachSource = async (path, cb) => {
	const entries = await readdir(path, {
		withFileTypes: true
	});
	for (const entry of entries) {
		if (entry.isFile() && entry.name === ".source.json") {
			const source = await readFile(`${path}/${entry.name}`, "utf8");
			await cb(path, JSON.parse(source));
		} else if (entry.isDirectory()) {
			await forEachSource(`${path}/${entry.name}`, cb);
		}
	}
};

const fetchBody = async (url, options) => {
	const res = await fetch(url, options);
	if (res.ok) {
		if (res.headers.get("content-type").includes("json")) {
			return res.json();
		} else {
			return res.text();
		}
	} else {
		throw new Error(`fetch ${url} -> ${res.status}: ${await res.text()}`);
	}
};

const github = async source => {
	const repoUrl = `https://api.github.com/repos/${source.repo}`;
	const repo = await fetchBody(repoUrl);
	const readme = await fetchBody(`${repoUrl}/readme`);
	const topics = await fetchBody(`${repoUrl}/topics`, {
		headers: {
			accept: "application/vnd.github.mercy-preview+json"
		}
	});
	const data = {
		date: repo.pushed_at,
		metadata: {
			title: repo.name,
			short_description: repo.description,
			tags: topics.names,
			homepage: repo.homepage,
			language: repo.language,
			license: repo.license.spdx_id
		}
	};

	return {
		readme: prepareGitHubReadme(readme),
		data
	};
};

const prepareGitHubReadme = readme =>
	Buffer.from(readme.content, readme.encoding)
		.toString()
		// Remove main heading
		.replace(/^# .+$/gm, "")
		// Make relative URLs absolute
		.replace(
			/\[([^\[\]]+)\]\(([^\)]+)\)/g,
			(_, p1, p2) => `[${p1}](${new URL(p2, readme.html_url)})`
		)
		// Replace language tags with PrismJS supported tags
		// https://prismjs.com/#supported-languages
		.replace(/```sh/g, "```shell-session")
		.replace(/```command/g, "```shell-session");

const sources = {
	github
};

const main = async () => {
	await forEachSource("src", async (path, source) => {
		if (sources[source.source]) {
			const { readme, data } = await sources[source.source](source);
			await writeFile(`${path}/index.md`, readme, "utf8");
			await writeFile(
				`${path}/index.11tydata.json`,
				JSON.stringify(data, null, "\t"),
				"utf8"
			);
		} else {
			console.warn(`Unknown source ${source.source} in ${path}`);
		}
	});
};

main().catch(err => {
	console.error(err);
	process.exitCode = 1;
});

@@ -19,16 +19,22 @@
 eadFile,
+ stat,
  writeFi
@@ -185,36 +185,8 @@
 path
-, %7B%0A%09%09withFileTypes: true%0A%09%7D
 );%0A%09
@@ -218,25 +218,102 @@
 es) %7B%0A%09%09
-if (entry
+const fullName = %60$%7Bpath%7D/$%7Bentry%7D%60;%0A%09%09const stats = await stat(fullName);%0A%09%09if (stats
 .isFile(
@@ -322,21 +322,16 @@
 && entry
-.name
  === %22.s
@@ -378,39 +378,24 @@
 eadFile(
-%60$%7Bpath%7D/$%7Bentry.n
+fullN
 ame
-%7D%60
 , %22utf8%22
@@ -449,21 +449,21 @@
 lse if (
-entry
+stats
 .isDirec
@@ -499,31 +499,16 @@
 rce(
-%60$%7Bpath%7D/$%7Bentry.n
+fullN
 ame
-%7D%60
 , cb

Make compatible with Node 10.8