src/builders/articles.tsx
import { jsx } from "@emotion/core";
// import getTOC from "markdown-toc";
import { render } from "../utils/render";
import { IState } from "../state";
import { check } from "../utils/cache";
import { writeFile, makeDir, del } from "../utils/fs";
import { parseMDX } from "../utils/parse-mdx";
import LayoutPage from "../components/layout";
import Document from "../components/document";
import Archive from "../components/archive";
import { IArticle } from "../getters/articles";
import { loadModule } from "../utils/load-module";
import { resolve, parse, join } from "path";
import { minify } from "terser";
/**
* Check if all articles are already cached
* @param state Current state of the project
*/
async function checkCache(state: IState): Promise<boolean> {
if (!state.config.incremental) return false;
const caches = await Promise.all(
Object.values(state.articles.byPath)
.map((resource: IArticle) => ({
path: resource.path,
content: resource.content
}))
.map(({ path, content }) => check(path, content))
);
return caches.reduce((prev, next) => next === prev, true);
}
function articleBuilder(state: IState) {
return async (article: IArticle) => {
if (await check(article.path, article)) return;
const [content] = await Promise.all([
parseMDX(article.content)
// article.toc
// ? parseMDX(getTOC(article.content).content)
// : Promise.resolve(null)
]);
await makeDir(resolve(parse(article.tmpPath).dir));
await Promise.all([
writeFile(resolve(article.tmpPath), content.code, "utf8")
// toc
// ? writeFile(article.tmpPath.concat(".toc.js"), toc.code, "utf8")
// : Promise.resolve(null)
]);
const Component = loadModule(article.tmpPath);
// const TOC = (article.toc
// ? loadModule(article.tmpPath.concat(".toc.js"))
// : Promise.resolve(null));
const renderedContent: string = await render(
<LayoutPage data={article} Component={Component} />,
{ state }
);
const html: string = await render(
<Document data={article} path={article.path} content={renderedContent} />,
{ state }
);
await makeDir(resolve(join("./public", article.path)));
await writeFile(
resolve(join("./public", article.path, "index.html")),
`<!DOCTYPE html>${html}`,
"utf8"
);
};
}
async function archiveBuilder(state: IState) {
if (state.articles.order.length === 0) {
return await del("public/articles/index.html");
}
const html = await render(
<Document path="/articles.mdx">
<Archive />
</Document>,
{ state }
);
await makeDir(resolve("./public/articles"));
await writeFile(
resolve("./public/articles/index.html"),
`<!DOCTYPE html>${html}`,
"utf8"
);
}
async function shareArticle() {
await writeFile(
"./public/share.js",
minify(
[
'if ("share" in window.navigator) {',
"const button = document.getElementById('share')",
'button.style.display = "inline-flex"',
"button.addEventListener('click', () => {",
"window.navigator.share({",
"title: document.title,",
"text: button.dataset.description,",
"url: location.href",
"});",
"})",
"}"
].join("\n")
).code
);
}
async function builder(state: IState) {
if ((await checkCache(state)) && (await check("config.yml", state.config))) {
return;
}
await Promise.all(
Object.values(state.articles.byPath)
.map(articleBuilder(state))
.concat(archiveBuilder(state))
.concat(shareArticle())
);
}
export { builder };