const handlebarsPlugin = require("@11ty/eleventy-plugin-handlebars"); const handlebars = require('handlebars'); const sass = require("sass"); const pluginRss = require("@11ty/eleventy-plugin-rss"); const handlebarsHelpers = require('handlebars-helpers') const markdownit = require('markdown-it') const md = markdownit() const htmlmin = require("html-minifier"); const { DateTime } = require("luxon"); require('dotenv').config(); module.exports = function(eleventyConfig) { // Passthrough episodes directory to include both markdown and audio files eleventyConfig.addPassthroughCopy("content/episodes/*/*.mp3"); eleventyConfig.addPassthroughCopy("content/episodes/**/*.jpg"); eleventyConfig.addPassthroughCopy("content/episodes/**/*.webp"); eleventyConfig.addPassthroughCopy("content/episodes/**/*.png"); eleventyConfig.addPassthroughCopy("content/images/*.jpg"); eleventyConfig.addPassthroughCopy("content/images/*.webp"); eleventyConfig.addPassthroughCopy("content/feeds/*.jpg"); eleventyConfig.addPlugin(handlebarsPlugin); eleventyConfig.addPlugin(pluginRss); handlebarsHelpers({ handlebars }) eleventyConfig.addFilter( "seasonEpisodeFormat", require("./filters/utils").seasonEpisodeFormat ); eleventyConfig.addFilter("formatDate", (date, format = "MMMM d, yyyy") => { return DateTime.fromJSDate(new Date(date)).toFormat(format); }); eleventyConfig.addFilter("episodeNumber", function (s, episode) { return episode ? Number(episode) : Number(s.replace(/[^0-9]/,'')) }); eleventyConfig.addFilter("episodesInSeason", function (season) { return this.eleventy.collection.episode.filter(ep=>ep.season == season) return episode ? Number(episode) : Number(s.replace(/[^0-9]/,'')) }); eleventyConfig.addFilter("extractUniqueTags", function (object, tagPrefix = "") { // Flatten all pages into a single array of items let allItems if (object.pages) {allItems = object.pages.flat()} else { object } // Extract the desired property from each item const extractedValues = allItems .map(item => item.data.tags) .flat(); // Flatten arrays if the property contains arrays, like tags // Filter for unique values with the "campaign" prefix const uniqueValues = [...new Set(extractedValues)] .filter(tag => tag.startsWith(tagPrefix)); return uniqueValues; }); eleventyConfig.addFilter("findPageByTag", function (tag, collections) { // Split the tag to get the collection name and slug const [collectionName, slug] = tag.split(":"); if (!collectionName || !slug) { console.error(`Invalid tag format: "${tag}". Expected format "collection:slug".`); return null; } // Ensure the collection exists const collection = collections[collectionName]; if (!collection) { console.error(`Collection "${collectionName}" not found in collections.`); return null; } // Search for the page in the inferred collection // TO-DO: this logic will break once we get into season 10, since season 1 will match 1 and 10 const matchingPage = collection.find(item => item.fileSlug.includes(slug)); // Return the matching page (or null if not found) return matchingPage || null; }); // Shortcodes eleventyConfig.addPairedShortcode( "prologue", function(content) { return `

Prologue


${md.render(content)}
` } ); eleventyConfig.addPairedShortcode( "masthead", function(content) { return `
${md.render(content)}
` } ); eleventyConfig.addPairedShortcode( "headline", function(content) { return `
${md.render(content.trim())}
` } ); eleventyConfig.addPairedShortcode( "alternateTitles", function(content) { return `

Alternate Titles


${md.render(content)}
` } ); eleventyConfig.addTransform("htmlmin", (content, outputPath) => { if (outputPath.endsWith(".html")) { return htmlmin.minify(content, { collapseWhitespace: true, removeComments: true, useShortDoctype: true, }); } return content; }); // Creates the extension for use eleventyConfig.addTemplateFormats("scss"); eleventyConfig.addExtension("scss", { outputFileExtension: "css", // optional, default: "html" // `compile` is called once per .scss file in the input directory compile: async function (inputContent) { let result = sass.compileString(inputContent, { loadPaths: ["node_modules/bootstrap/scss", ] }); // This is the render function, `data` is the full data cascade return async (data) => result.css; }, }); return { pathPrefix:"blog", dir: { data: "data", input: "content", includes: "../layouts" } }; };