-
Notifications
You must be signed in to change notification settings - Fork 5
/
.eleventy.js
238 lines (201 loc) · 7.34 KB
/
.eleventy.js
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
const { DateTime } = require("luxon");
const CleanCSS = require("clean-css");
const UglifyJS = require("uglify-js");
const htmlmin = require("html-minifier");
const yaml = require("js-yaml");
const slugify = require("slugify");
const eleventyHelmetPlugin = require("eleventy-plugin-helmet");
const EleventyFetch = require("@11ty/eleventy-fetch");
const Image = require("@11ty/eleventy-img");
const MarkdownIt = require("markdown-it");
const mdRender = new MarkdownIt();
module.exports = function(eleventyConfig) {
eleventyConfig.addFilter("renderUsingMarkdown", function(rawString) {
return mdRender.render(rawString);
});
// https://www.11ty.dev/docs/plugins/image/
// Generate PNG icon files and a link tag from a source SVG or PNG file
eleventyConfig.addShortcode("favicon", async function(src) {
// Remove preceding slash from image path if it exists
src = src.startsWith("/") ? src.slice(1) : src;
let metadata = await Image(src, {
widths: [48,192,512],
formats: ["png"],
urlPath: "/",
outputDir: "./_site/",
filenameFormat: function (id, src, width, format, options) {
const name = "favicon";
return `${name}-${width}.${format}`;
}
});
// Build the icon link tag
let data = metadata.png[0];
return `<link rel="icon" href="${data.url}" type="image/png">`;
});
// Shortcode to generate a responsive project image
eleventyConfig.addShortcode("generateImage", async function(params) {
// Destructure the paramaters object and set some defaults
let {
src, // throw an error if src is missing
alt = "",
classes = "",
loadingType = "lazy",
viewportSizes = "",
outputWidths = ["1080","1800","2400"],
outputFormats = ["jpeg"],
outputQualityJpeg = 75,
outputQualityWebp = 75,
outputQualityAvif = 75
} = params;
// Tina CMS prefixes uploaded img src with a forward slash (?)
// Remove it from the image path if it exists
src = src.startsWith("/") ? src.slice(1) : src;
let metadata = await Image(src, {
widths: outputWidths,
sharpJpegOptions: { quality: outputQualityJpeg },
sharpWebpOptions: { quality: outputQualityWebp },
sharpAvifOptions: { quality: outputQualityAvif },
formats: outputFormats,
urlPath: "/assets/images/",
outputDir: "./_site/assets/images/",
cacheOptions: {
// If image is a remote URL, this is the amount of time before 11ty fetches a fresh copy
duration: "5y",
directory: ".cache",
removeUrlQueryParams: true,
},
});
let lowsrc = metadata.jpeg[0];
let orientation;
// Detect and set image orientation
if (lowsrc.width > lowsrc.height) {
orientation = "landscape";
} else if (lowsrc.width < lowsrc.height) {
orientation = "portrait";
} else {
orientation = "square";
}
return `<picture class="${classes}" data-orientation="${orientation}">
${Object.values(metadata).map(imageFormat => {
return ` <source type="${imageFormat[0].sourceType}" srcset="${imageFormat.map(entry => entry.srcset).join(", ")}" sizes="${viewportSizes}">`;
}).join("\n")}
<img
src="${lowsrc.url}"
width="${lowsrc.width}"
height="${lowsrc.height}"
alt="${alt}"
class="hover-fade"
loading="${loadingType}"
decoding="async">
</picture>`;
});
// Add 11ty helmet plugin, for appending elements to <head>
eleventyConfig.addPlugin(eleventyHelmetPlugin);
// Add support for YAML data files with .yaml extension
eleventyConfig.addDataExtension("yaml", contents => yaml.load(contents));
// Merge 11ty data instead of overriding values
eleventyConfig.setDataDeepMerge(true);
// The projects collection, sorted by the numerical position value and then by date
eleventyConfig.addCollection("projects", function(collectionApi) {
return collectionApi.getFilteredByGlob("projects/*.md")
//.filter(project => !Boolean(project.data.draft))
.sort((a, b) => b.data.position - a.data.position);
});
// A filter to limit output of collection items
eleventyConfig.addFilter("limit", function (arr, limit) {
return arr.slice(0, limit);
});
// A filter to limit and randomize output of collection items
eleventyConfig.addFilter("randomLimit", (arr, limit, currPage) => {
const pageArr = arr.filter((page) => page.url !== currPage);
pageArr.sort(() => {
return 0.5 - Math.random();
});
return pageArr.slice(0, limit);
});
// Filter to format Google Fonts font name for use in link URLs
eleventyConfig.addFilter("formatGoogleFontName", name => {
return name.replace(/\s/g, '+');
});
// Date formatting (human readable)
eleventyConfig.addFilter("dateFullYear", dateObj => {
return DateTime.fromJSDate(dateObj).toFormat("yyyy");
});
// base64 encode a string
eleventyConfig.addFilter("encodeURL", function(url) {
return encodeURIComponent(url);
});
// Minify CSS
eleventyConfig.addFilter("cssmin", function(code) {
return new CleanCSS({}).minify(code).styles;
});
// Minify JS
eleventyConfig.addFilter("jsmin", function(code) {
let minified = UglifyJS.minify(code);
if (minified.error) {
console.log("UglifyJS error: ", minified.error);
return code;
}
return minified.code;
});
// Minify HTML output
eleventyConfig.addTransform("htmlmin", function(content, outputPath) {
if (outputPath.indexOf(".html") > -1) {
let minified = htmlmin.minify(content, {
useShortDoctype: true,
removeComments: true,
collapseWhitespace: true
});
return minified;
}
return content;
});
// Create a hash from date (e.g. for permalinks)
eleventyConfig.addFilter("hashFromDate", dateObj => {
return new Number(DateTime.fromJSDate(dateObj)).toString(36);
});
// Universal slug filter makes-strict-urls-like-this
eleventyConfig.addFilter("slug", function(str) {
return slugify(str, {
lower: true,
replacement: "-",
strict: true
});
});
// Shortcode to download, cache, and minify Google Fonts CSS to reduce HTTP requests on the front-end
// TODO Consider downloading the font file itself and storing in the build cache
eleventyConfig.addShortcode("googleFontsCss", async function(url) {
let fontCss = await EleventyFetch(url, {
duration: "1d",
type: "text",
fetchOptions: {
headers: {
// Google Fonts API serves font formats based on the browser user-agent header
// So here we pretend to be a browser... in this case, Chrome 74 on MacOS 14
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
}
}
});
//return fontCss;
return new CleanCSS({}).minify(fontCss).styles;
});
// Copy folders or static assets e.g. images to site output
eleventyConfig.addPassthroughCopy({"assets/icons/favicon.svg" : "/favicon.svg"});
// Disable 11ty dev server live reload when using CMS locally
eleventyConfig.setServerOptions({
liveReload: false
});
return {
templateFormats: ["md", "njk", "liquid"],
pathPrefix: "/",
markdownTemplateEngine: "njk",
htmlTemplateEngine: "njk",
dataTemplateEngine: "njk",
dir: {
input: ".",
includes: "_includes",
data: "_data",
output: "_site"
}
};
};