summaryrefslogtreecommitdiff
path: root/.eleventy.js
blob: 6cb78a04da4f569e1d90267fe7ab7d19aaaf7e0d (plain)
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
const fs = require("fs");
const hljs = require("highlight.js");
const twemoji = require("twemoji");
const { DateTime } = require("luxon");
const utils = require("markdown-it/lib/common/utils");

const excerptMinimumLength = 280;
const excerptSeparator = "<!--more-->";

const twemojiOpts = {
  base: "https://cdnjs.cloudflare.com/ajax/libs/twemoji/15.1.0/",
};

function extractExcerpt(doc) {
  if (doc.data.readOnline) return "[[ No Excerpt Available ]]";
  if (doc.data.excerpt) return doc.data.excerpt;

  if (!doc.hasOwnProperty("templateContent")) {
    console.warn("Failed to extract excerpt: Document has no property `templateContent`.");
    return "";
  }

  const content = doc.templateContent;
  if (content.includes(excerptSeparator))
    return content.substring(0, content.indexOf(excerptSeparator)).trim();
  else if (content.length <= excerptMinimumLength) return content.trim();

  const excerptEnd = findExcerptEnd(content);
  return content.substring(0, excerptEnd).trim();
}

function findExcerptEnd(content, skipLength = 0) {
  if (content === "") return 0;
  const paragraphEnd = content.indexOf("</p>", skipLength) + 4;
  return paragraphEnd + ((paragraphEnd < excerptMinimumLength)?
    findExcerptEnd(content.substring(paragraphEnd), paragraphEnd):0);
}

function demark(content) {
  return content.replace(/<mark>(.*?)<\/mark>/gis, (m,a) => "&#x2588;".repeat(a.length))
}

module.exports = (cfg) => {
  cfg.addPlugin(require("@11ty/eleventy-plugin-rss"));
  cfg.addFilter("excerpt", post => extractExcerpt(post));
  cfg.addFilter("demark", content => demark(content));
  cfg.addFilter("date", (date, fmt) =>
    DateTime.fromJSDate(date).toFormat(fmt));
  cfg.addPassthroughCopy({"static": "./"});
  cfg.setDataDeepMerge(true);

  cfg.addFilter("fixAnchors", (c,u) =>
    c.replace(/<a class="header-anchor" href="/g, "$&"+u));

  const md = require("markdown-it")({
    typographer: true, html: true,
    highlight: (str, lang) => {
      if (lang && hljs.getLanguage(lang)) { try {
        return '<pre class="hljs"><code>' +
          hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
        '</code></pre>';
      } catch (__) {} }
      return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
    }

  }).use(require("markdown-it-anchor"), {
      permalink: true, permalinkBefore: true, permalinkSymbol: "#"})
    .use(require("markdown-it-emoji"), {shortcuts: {}})
    .use(require("markdown-it-mark"));

  md.renderer.rules.emoji = (token, i) =>
    `<span class="emoji-container emoji_${token[i].markup}" ` +
      `title="${token[i].markup}">${twemoji.parse(token[i].content, twemojiOpts)}</span>`;
  md.renderer.rules.code_block = (tokens, i, options, env, slf) =>
    `<pre${slf.renderAttrs(tokens[i])}><code>` +
      utils.escapeHtml(tokens[i].content.replace(/ /g, "\xA0"))+'</code></pre>\n';

  cfg.setLibrary("md", md);

  cfg.setBrowserSyncConfig({callbacks: {
    ready: (e,b) => b.addMiddleware("*", (req, res) => {
      res.writeHead(404, { "Content-Type": "text/html; charset=UTF-8" });
      res.write(fs.readFileSync("build/404.html"));
      res.end();
    })
  }});

  return {
    templateFormats: ["md", "njk"],
    markdownTemplateEngine: "njk",
    dir: {
      input: "posts",
      output: "build",
      data: "../globals",
      layouts: "../layouts",
      includes: "../includes",
    },
  };
};