]> Witch of Git - web/blog/blob - eleventy.config.js
Move to an ES module for config
[web/blog] / eleventy.config.js
1 import pluginRss from "@11ty/eleventy-plugin-rss";
2 import pluginSyntaxHighlight from "@11ty/eleventy-plugin-syntaxhighlight";
3 import dateFilter from "nunjucks-date-filter";
4 import uslug from "uslug";
5 import anchor from "markdown-it-anchor";
6 import markdownIt from "markdown-it";
7 import CleanCSS from "clean-css";
8 import htmlMinifier from "html-minifier-terser";
9 import util from "util";
10
11 const md = markdownIt({
12 html: true,
13 typographer: true,
14 }).use(anchor, {
15 slugify: s => uslug(s),
16 permalink: true,
17 permalinkBefore: true,
18 permalinkClass: "header-anchor c-sun dec-none",
19 });
20
21 export default async function (eleventyConfig) {
22 eleventyConfig.addNunjucksFilter('date', dateFilter);
23 // @TODO: Update to the new virtual template setup?
24 eleventyConfig.addPlugin(pluginRss);
25 eleventyConfig.addPlugin(pluginSyntaxHighlight);
26
27 eleventyConfig.setLibrary("md", md);
28
29 eleventyConfig.addTransform("html-minifier", htmlMinifierTransform);
30
31 eleventyConfig.addPassthroughCopy("img");
32 eleventyConfig.addPassthroughCopy("static");
33 eleventyConfig.addPassthroughCopy("stamps");
34 eleventyConfig.addPassthroughCopy({ "favicon": "/" });
35
36 eleventyConfig.addNunjucksShortcode("youtube", youtubeShortcode);
37 eleventyConfig.addPairedNunjucksShortcode("tweet", tweetShortcode);
38 eleventyConfig.addPairedShortcode("aside", asideShortcode);
39 eleventyConfig.addPairedShortcode("figure", figureShortcode);
40
41 eleventyConfig.addFilter("markdown", value => md.renderInline(value));
42 eleventyConfig.addFilter("groupby", groupbyFilter);
43 eleventyConfig.addFilter("cssmin", css =>
44 new CleanCSS({}).minify(css).styles);
45 eleventyConfig.addFilter("debug", util.inspect);
46
47 eleventyConfig.setDataDeepMerge(true);
48
49 eleventyConfig.addCollection("years", collection => {
50 const posts = collection.getFilteredByTag("posts");
51 const items = groupby(posts, item => item.date.getFullYear());
52 return items.reduce((obj, [k, v]) => (obj[k] = v, obj), {});
53 });
54
55 return {
56 markdownTemplateEngine: "njk",
57 };
58 };
59
60 function access(item, path) {
61 const segments = path.split(".");
62 for (const seg of segments) {
63 if (item === undefined) { return null; }
64 if (seg.endsWith("()")) {
65 const method = item[seg.slice(0, -2)];
66 if (method === undefined) { return null; }
67 item = method.bind(item)();
68 } else {
69 item = item[seg];
70 }
71 }
72 return item;
73 }
74
75 function groupby(items, keyFn) {
76 const results = [];
77 for (const item of items) {
78 const key = keyFn(item);
79 if (results.length == 0 || key != results[results.length-1][0]) {
80 results.push([key, [item]]);
81 } else {
82 results[results.length-1][1].push(item);
83 }
84 }
85 return results;
86 }
87
88 function groupbyFilter(items, path) {
89 return groupby(items, item => access(item, path));
90 }
91
92 function youtubeShortcode(items, inWidth = 560, inHeight = 315) {
93 const width = items.width || inWidth;
94 const height = items.height || inHeight;
95 const allow = items.allow || "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
96 const border = items.border || "0";
97 const video = items.video || items;
98 if (!video) {
99 throw "Required argument 'video'.";
100 }
101 const src = "https://www.youtube.com/embed/" + video;
102 return `<div class="youtube row hcenter">
103 <iframe width="${width}" height="${height}" src="${src}"
104 frameborder="${border}" allow="${allow}" allowfullscreen></iframe>
105 </div>`;
106 }
107
108 function tweetShortcode(content, items) {
109 // @TODO: Handle parsing date
110 return `<div class="row hcenter">
111 <blockquote class="twitter-tweet">
112 <p lang="en" dir="ltr">${content}</p> &mdash; ${items.name} (@${items.at})
113 <a href="${items.link}">${items.date}</a>
114 </blockquote>
115 <script async src="https://platform.twitter.com/widgets.js" charset="utf-8">
116 </script>
117 </div>`;
118 }
119
120 function asideShortcode(content, style='') {
121 const html = md.render(content);
122 if (style) {
123 return `<aside class="${style}">${html}</aside>`;
124 } else {
125 return `<aside>${html}</aside>`;
126 }
127 }
128
129 function figureShortcode(content, { src, alt }) {
130 const captionHtml = md.render(content);
131 return `<figure class="col hcenter">
132 <img src="${src}" alt="${alt}">
133 <figcaption>${captionHtml}</figcaption>
134 </figure>`;
135 }
136
137 function htmlMinifierTransform(content, outputPath) {
138 if (outputPath.endsWith(".html")) {
139 return htmlMinifier.minify(content, {
140 useShortDoctype: true,
141 removeComments: true,
142 collapseWhitespace: true,
143 });
144 }
145 return content;
146 }