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