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