Animated gifs can't go through the asset pipeline since they come out as a single frame of the original animation. Previously, I had only a couple of them and didn't have my assets structured very well. I had the assets that needed to be passthrough copied alongside those that could go through the pipeline. It wasn't bad since there weren't many animations. I mapped just those to passthrough copy to go to a single directory. This will become more fraught as I add more since there's more opportunity for collisions. When mapping to an output directory, I can't maintain the inner structure of that directory. The files are flattened. I moved everything around, both to separate images that can go through the pipeline from those which can't and to allow for a simple passthrough copy that will maintain the directory structure so that each blog post can have its own directory of passthrough copied animated gifs in the build.
138 lines
4.9 KiB
JavaScript
138 lines
4.9 KiB
JavaScript
const path = require("path");
|
|
const markdownIt = require("markdown-it");
|
|
const slugify = require("@sindresorhus/slugify");
|
|
const markdownItAnchor = require("markdown-it-anchor");
|
|
const markdownItObsidian = require("markdown-it-obsidian");
|
|
const markdownItAttrs = require("markdown-it-attrs");
|
|
const markdownItEleventyImg = require("markdown-it-eleventy-img");
|
|
const pluginRss = require("@11ty/eleventy-plugin-rss");
|
|
const embedYouTube = require("eleventy-plugin-youtube-embed");
|
|
const sanitizeHtml = require("sanitize-html");
|
|
const metadata = require("./src/_data/metadata.json");
|
|
|
|
const sanitize = (html) => {
|
|
return sanitizeHtml(html, {
|
|
allowedTags: ['b', 'i', 'em', 'strong', 'a'],
|
|
allowedAttributes: { 'a': ['href'] }
|
|
});
|
|
};
|
|
|
|
module.exports = function(eleventyConfig) {
|
|
eleventyConfig.addPlugin(embedYouTube);
|
|
eleventyConfig.addPlugin(pluginRss);
|
|
|
|
eleventyConfig.addPassthroughCopy("css");
|
|
eleventyConfig.addPassthroughCopy("fonts");
|
|
eleventyConfig.addPassthroughCopy("images/**/*.{gif,png,jpg,jpeg}");
|
|
eleventyConfig.addPassthroughCopy({ "favicon": "/" });
|
|
eleventyConfig.addPassthroughCopy({
|
|
"node_modules/simpledotcss/simple.css": "css/simple.css",
|
|
});
|
|
eleventyConfig.addPassthroughCopy({ "src/robots.txt": "robots.txt" });
|
|
|
|
eleventyConfig.addFilter("buildPageTitle", function(title) {
|
|
if (this.page.url === '/') {
|
|
return title;
|
|
} else {
|
|
return title + ' ⇢ Devon.LoL 😆';
|
|
}
|
|
});
|
|
eleventyConfig.addFilter("stringToDate", function(value) {
|
|
return new Date(value);
|
|
});
|
|
eleventyConfig.addFilter("formatDatetimeForHumans", function(value) {
|
|
const dt = new Date(value);
|
|
const formattedDt = new Intl.DateTimeFormat("en", {
|
|
dateStyle: "medium",
|
|
timeStyle: "medium",
|
|
}).format(dt);
|
|
return formattedDt;
|
|
});
|
|
eleventyConfig.addFilter("formatDateForHumans", function(value) {
|
|
const dt = new Date(value);
|
|
const formattedDt = new Intl.DateTimeFormat("en", {
|
|
dateStyle: "medium",
|
|
}).format(dt);
|
|
return formattedDt;
|
|
});
|
|
eleventyConfig.addFilter("formatDateForRobots", function(value) {
|
|
const dt = new Date(value);
|
|
return dt.toISOString();
|
|
});
|
|
eleventyConfig.addFilter("convertYouTubeURLsToEmbeds", function(value) {
|
|
const youtubeUrlRegex = /<p>\s*(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?!channel\/|@)([^\s&<>"']*)\s*<\/p>/gim;
|
|
return value.replace(youtubeUrlRegex, function(match, videoId) {
|
|
return '<iframe width="560" height="315" src="https://www.youtube.com/embed/' + videoId + '" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>';
|
|
});
|
|
});
|
|
eleventyConfig.addFilter('webmentionsByUrl', function(webmentions, url) {
|
|
const allowedTypes = ['in-reply-to', 'like-of', 'repost-of'];
|
|
const hasRequiredFields = mention => {
|
|
const { author, published, content } = mention;
|
|
return author.name && published && content;
|
|
};
|
|
const matchesUrl = mention => {
|
|
return mention['wm-target'] === url;
|
|
};
|
|
|
|
const filteredMentions = webmentions
|
|
.filter(mention => {
|
|
const isAllowedType = allowedTypes.includes(mention['wm-property']);
|
|
if (isAllowedType) {
|
|
const isReply = mention['wm-property'] === 'in-reply-to';
|
|
if (isReply) {
|
|
return matchesUrl(mention) && hasRequiredFields(mention);
|
|
}
|
|
return matchesUrl(mention);
|
|
}
|
|
}
|
|
);
|
|
const sanitizedMentions = filteredMentions.map((mention) => {
|
|
if (mention?.content?.html) {
|
|
mention.content.html = sanitize(mention.content.html);
|
|
const mastodonInstance = metadata.author.mastodon.instance;
|
|
const mastodonUsername = metadata.author.mastodon.username;
|
|
const mentionLink = `<a href="https://${mastodonInstance}/@${mastodonUsername}">@${metadata.author.mastodon.username}</a> `;
|
|
if (mention.content.html.startsWith(mentionLink)) {
|
|
mention.content.html = mention.content.html.substring(mentionLink.length)
|
|
}
|
|
}
|
|
return mention;
|
|
});
|
|
const sortedMentions = sanitizedMentions.reduce((accumulator, mention) => {
|
|
const key = mention['wm-property'];
|
|
const curGroup = accumulator[key] ?? [];
|
|
return { ...accumulator, [key]: [...curGroup, mention] };
|
|
}, {});
|
|
|
|
return sortedMentions;
|
|
});
|
|
|
|
const markdownLibrary = markdownIt({ html: true })
|
|
.use(markdownItAnchor, { slugify })
|
|
.use(
|
|
markdownItObsidian({
|
|
slugifyOnPageLinks: slugify,
|
|
})
|
|
)
|
|
.use(markdownItAttrs)
|
|
.use(markdownItEleventyImg, {
|
|
imgOptions: {
|
|
widths: [75, 150, 300, 600, 900, 1100],
|
|
outputDir: path.join("dist", "img"),
|
|
},
|
|
globalAttributes: {
|
|
loading: "lazy",
|
|
decoding: "async",
|
|
sizes: "100vw",
|
|
},
|
|
});
|
|
eleventyConfig.setLibrary("md", markdownLibrary);
|
|
|
|
return {
|
|
dir: {
|
|
input: "src",
|
|
output: "dist",
|
|
},
|
|
};
|
|
};
|