all repos — kushiyaki @ main

🍢 A tiny static site generator for grilling markdown files to perfection

index.js (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
#!/usr/bin/env bun
import cpy from "cpy";
import double from "./addons/double.js"; //TODO add systemized plugin support
import insertJSX from "./insertJSX.js";
import rehypeDocument from "rehype-document";
import rehypeFormat from "rehype-format";
import rehypeRaw from "rehype-raw";
import rehypeStringify from "rehype-stringify";
import rehypeWrap from "rehype-wrap";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import { emptyDir } from "fs-extra";
import { glob } from "glob";
import { matter } from "vfile-matter";
import { read, write } from "to-vfile";
import { resolve } from "path";
import { unified } from "unified";
import { reporterPretty } from "vfile-reporter-pretty";

const skippedFiles = [];
function errCatch(file, error) {
  file.message(error);
  skippedFiles.push(file);
  return null;
}

// Resolve input/putput directories respecting a project's root folder.
const inputDir = resolve(process.cwd(), "input");
const outputDir = resolve(process.cwd(), "out");
const site = await glob(`${inputDir}/**/*.md`, { nodir: true });

// mise en place
emptyDir(`${outputDir}`, { recursive: true }); // clears the output folder
await cpy([`${inputDir}/**`, `!${inputDir}/**/*.md`], `${outputDir}`, {
  flat: true,
}); //copies all assets into out/

async function createSiteIndex() {
  return (
    await Promise.all(
      site.map(async (page) => {
        let file = await read(page, "utf-8");
        try {
          matter(file);
          file.data.matter.filename = file.stem;
          return file.data.matter;
        } catch (e) {
          errCatch(file, e);
        }
      }),
    )
  ).filter(Boolean);
}
let ticker = 0;
export async function kushiyaki(filepath, siteIndex) {
  const file = await read(filepath, "utf-8");
  try {
    matter(file, { strip: true });
    file.data.matter.filename = file.stem;
  } catch (e) {
    return null;
  }

  const processor = unified()
    .data("siteIndex", siteIndex)
    .use(remarkParse)
    .use(double)
    .use(remarkRehype, { allowDangerousHtml: true })
    .use(rehypeRaw)
    .use(rehypeWrap, { wrapper: "main" })
    .use(insertJSX, file.data.matter)
    .use(rehypeDocument, {
      css: file.data.matter?.css ?? "style.css",
      title: file.data.matter?.title ?? "untitled",
      link: [{ href: "favicon.png", rel: "icon", type: "image/png" }], //FIX make filetype-agnostic
    })
    .use(rehypeFormat)
    .use(rehypeStringify);

  try {
    const output = await processor.process(file);
    output.extname = ".html";
    output.dirname = outputDir;
    ticker++;
    return output;
  } catch (e) {
    errCatch(file, e);
  }
}

// Loop through function for each page
const siteIndex = await createSiteIndex();
for (const file of site) {
  const output = await kushiyaki(file, siteIndex);
  if (output) await write(output);
}
console.log(reporterPretty(skippedFiles));
console.log("----------------\n" + ticker + " pages successfully written");
console.error(skippedFiles.length + " files skipped");