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