src/builders/slides.tsx
import { jsx } from "@emotion/core";
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 SlidePage from "../components/slide";
import Document from "../components/document";
import SlidesPage from "../components/slides";
import { loadModule } from "../utils/load-module";
import { resolve, join } from "path";
import { ISlide } from "../getters/slides";
import { minify } from "terser";
import { BabelFileResult } from "babel-core";
/**
* Check if all slides 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.slides.byPath)
.map(resource => ({
path: resource.path,
content: resource.content
}))
.map(({ path, content }) => check(path, content))
);
return caches.reduce((prev, next) => next === prev, true);
}
async function slideScriptBuilder(state: IState) {
if (state.slides.order.length === 0) {
return await del("public/slide.js");
}
await writeFile(
"./public/slide.js",
minify(
[
"const totalSlides = parseInt(",
' document.getElementById("slide").dataset.total,',
" 10",
");",
"function next(pathname) {",
' if (pathname.endsWith("/")) return pathname + 1',
' const number = parseInt(pathname.slice(pathname.lastIndexOf("/") + 1), 10) + 1;',
" if (number > totalSlides) return pathname;",
' return pathname.slice(0, pathname.lastIndexOf("/") + 1) + number;',
"}",
"function prev(pathname) {",
' if (pathname.endsWith("/")) return pathname;',
' const number = parseInt(pathname.slice(pathname.lastIndexOf("/") + 1), 10) - 1;',
' return pathname.slice(0, pathname.lastIndexOf("/") + 1) + (number === 0 ? "" : number);',
"}",
'window.addEventListener("keydown", ({ key }) => {',
' if (key === "ArrowRight") window.location.pathname = next(window.location.pathname);',
' if (key === "ArrowLeft") window.location.pathname = prev(window.location.pathname);',
"})"
].join("\n")
).code
);
}
function slideBuilder(state: IState) {
return async (slide: ISlide) => {
if (await check(slide.path, slide)) return;
const slides = await Promise.all(slide.content.split("---").map(parseMDX));
await makeDir(resolve(slide.tmpPath));
await Promise.all(
slides.map((content: BabelFileResult, index: number) =>
writeFile(join(slide.tmpPath, `${index}.js`), content.code, "utf8")
)
);
await Promise.all(
slides.map(async (_, index) => {
const Component = loadModule(join(slide.tmpPath, `${index}.js`));
const html: string = await render(
<Document data={slide} path={slide.path}>
<SlidePage Component={Component} totalSlides={slides.length - 1} />
</Document>,
{ state }
);
await makeDir(resolve(join("./public", slide.path)));
await writeFile(
resolve(
join(
"./public",
slide.path,
`${index === 0 ? "index" : index}.html`
)
),
`<!DOCTYPE html>${html}`,
"utf8"
);
})
);
};
}
async function slideListBuilder(state: IState) {
if (state.slides.order.length === 0) {
return await del("public/slides/index.html");
}
const html = await render(
<Document path="/slides.mdx">
<SlidesPage />
</Document>,
{ state }
);
await makeDir(resolve("./public/slides"));
await writeFile(
resolve("./public/slides/index.html"),
`<!DOCTYPE html>${html}`,
"utf8"
);
}
async function builder(state: IState) {
if ((await checkCache(state)) && (await check("config.yml", state.config))) {
return;
}
await Promise.all(
Object.values(state.slides.byPath)
.map(slideBuilder(state))
.concat(slideScriptBuilder(state))
.concat(slideListBuilder(state))
);
}
export { builder };