const siteURL = "https://tarasis.net"; const fs = require("fs-extra"); const sass = require("sass"); const { promisify } = require("util"); const sassRender = promisify(sass.render); const postcss = require("postcss"); const autoprefixer = require("autoprefixer"); const markdownIt = require("markdown-it"); const markdownItRenderer = new markdownIt(); const markdownItAnchor = require("markdown-it-anchor"); // const relativeUrl = require("eleventy-filter-relative-url"); const pluginTOC = require("eleventy-plugin-toc"); const moment = require("moment"); const description = require("eleventy-plugin-description"); const pluginRss = require("@11ty/eleventy-plugin-rss"); const UpgradeHelper = require("@11ty/eleventy-upgrade-help"); const xmlFiltersPlugin = require("eleventy-xml-plugin"); const yaml = require("js-yaml"); //const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight"); const syntaxHighlight = require("eleventy-plugin-syntaxhighlight-chroma"); const highlight = require("chroma-highlight"); const eleventySass = require("@11tyrocks/eleventy-plugin-sass-lightningcss"); const inspect = require("node:util").inspect; // relativeURL const path = require("path"); const urlFilter = require("@11ty/eleventy/src/Filters/Url"); const { assert } = require("console"); const indexify = (url) => url.replace(/(\/[^.]*)$/, "$1index.html"); module.exports = function (eleventyConfig) { let pathPrefix = "/"; eleventyConfig.addDataExtension("yaml", (contents) => yaml.load(contents)); eleventyConfig.addDataExtension("yml", (contents) => yaml.load(contents)); eleventyConfig.addPlugin(pluginRss); //Blog excerpts eleventyConfig.addPlugin(description); // Eleventy Navigation (https://www.11ty.dev/docs/plugins/navigation/) eleventyConfig.addPlugin(require("@11ty/eleventy-navigation")); // Eleventy RSS Feed (https://www.11ty.dev/docs/plugins/rss/) eleventyConfig.addPlugin(require("@11ty/eleventy-plugin-rss")); // Filter to generate a Table of Contents from page content eleventyConfig.addPlugin(pluginTOC, { tags: ["h2", "h3"], wrapper: "div", }); // TODO https://www.npmjs.com/package/eleventy-plugin-meta-generator // Eleventy Syntax Highlighting (https://www.11ty.dev/docs/plugins/syntaxhighlight/) // eleventyConfig.addPlugin(require("@11ty/eleventy-plugin-syntaxhighlight")); eleventyConfig.addPlugin(syntaxHighlight, { theme: "base16-snazzy", lexerOverrides: { njk: "vue", liquid: "vue", }, }); // eleventyConfig.addPlugin(syntaxHighlight, { // alwaysWrapLineHighlights: true, // // Change which Eleventy template formats use syntax highlighters // // templateFormats: ["*"], // default // // Use only a subset of template types (11ty.js added in v4.0.0) // // templateFormats: ["liquid", "njk", "md", "11ty.js"], // // init callback lets you customize Prism // // init: function({ Prism }) { // // Prism.languages.myCustomLanguage = /* */; // // }, // // Added in 3.1.1, add HTML attributes to the
 or  tags
    //     preAttributes: {
    //       tabindex: 0,

    //       // Added in 4.1.0 you can use callback functions too
    //       "data-language": function({ language, content, options }) {
    //         return language;
    //       }
    //     },
    //     codeAttributes: {},
    //   });

    // assert(typeof highlight === "function");
    // eleventyConfig.addPlugin(highlight);

    // eleventyConfig.addMarkdownHighlighter(
    //     highlight(
    //         `--formatter html --html-only --html-inline-styles --lexer typescript --style base16-snazzy`
    //     )
    // );

    eleventyConfig.addPlugin(xmlFiltersPlugin);

    // eleventyConfig.addPlugin(eleventySass);

    // Custom Collections
    eleventyConfig.addCollection("pages", (collection) =>
        collection.getFilteredByGlob("./src/_pages/**/*")
    );

    eleventyConfig.addCollection("posts", (collection) =>
        collection
            .getFilteredByGlob("./src/_posts/**/*")
            .filter(
                (item) => item.data.draft !== true && item.date <= new Date()
            )
            .reverse()
            .map((cur, i, all) => {
                cur.data["siblings"] = {
                    next: all[i - 1],
                    prev: all[i + 1],
                };
                return cur;
            })
    );

    eleventyConfig.addCollection("projects", (collection) =>
        collection
            .getFilteredByGlob("./src/projects/**/*")
            .sort((a, b) => a.data.weight - b.data.weight)
    );

    // eleventyConfig.addCollection("posts", function (collectionApi) {
    //     return collectionApi.getFilteredByGlob("./src/_posts/**/*.md");
    // });

    eleventyConfig.addCollection("drafts", (collection) =>
        collection
            .getFilteredByGlob("./src/_drafts/**/*")
            .sort((a, b) => a.data.weight - b.data.weight)
    );

    eleventyConfig.addCollection("tags", (collection) => {
        let tags = new Set();

        collection.getAll().forEach((item) => {
            if ("tags" in item.data) {
                if (item.data.tags != undefined) {
                    for (const tag of item.data.tags) {
                        tags.add(tag);
                    }
                }
            }
        });

        return [...tags];
    });

    eleventyConfig.addCollection("categories", (collection) => {
        let categories = new Set();

        collection.getAll().forEach((item) => {
            if ("category" in item.data) {
                if (item.data.category != undefined) {
                    for (const category of item.data.category) {
                        categories.add(category);
                    }
                }
            }
        });

        return [...categories];
    });

    // Filters

    // eleventyConfig.addFilter("markdownify", (str) => {
    //     return markdownItRenderer.renderInline(str);
    // });

    eleventyConfig.addFilter("markdownify", (string) => {
        return md.renderInline(string);
    });

    eleventyConfig.addNunjucksFilter("markdownify", (str) => md.render(str));

    eleventyConfig.addFilter("jsonify", (variable) => JSON.stringify(variable));

    eleventyConfig.addFilter("slugify", (str) =>
        require("slugify")(str, {
            lower: true,
            replacement: "-",
            remove: /[*+~.·,()''`´%!?¿:@]/g,
        })
    );

    eleventyConfig.addFilter("where", (array, key, value) =>
        array.filter((item) => {
            const keys = key.split(".");
            const reducedKey = keys.reduce((object, key) => object[key], item);

            return reducedKey === value ? item : false;
        })
    );

    eleventyConfig.addFilter("date", (date, format = "") =>
        require("moment")(date).format(format)
    );

    eleventyConfig.addLiquidFilter("toUTCString", (date) => {
        const utc = date.toUTCString();
        return moment.utc(utc).format("MMMM Do YYYY");
    });

    eleventyConfig.addFilter("number_of_words", numberOfWords);

    // eleventyConfig.addShortcode("where_exp", function (item, exp) {
    //     console.log(exp);
    //     return eval(exp);
    // });
    eleventyConfig.addFilter("where_exp", function (value) {
        // where_exp function
        return value.hidden != true;
    });

    eleventyConfig.addFilter("inspect", function (obj = {}) {
        return inspect(obj, { sorted: true });
    });

    eleventyConfig.addFilter("group_by", groupBy);

    eleventyConfig.addLayoutAlias(
        "archive-taxonomy",
        "layouts/archive-taxonomy.html"
    );
    eleventyConfig.addLayoutAlias("archive", "layouts/archive.html");
    eleventyConfig.addLayoutAlias("categories", "layouts/categories.html");
    eleventyConfig.addLayoutAlias("category", "layouts/category.html");
    eleventyConfig.addLayoutAlias("collection", "layouts/collection.html");
    eleventyConfig.addLayoutAlias("compress", "layouts/compress.html");
    eleventyConfig.addLayoutAlias("default", "layouts/default.html");
    eleventyConfig.addLayoutAlias("home", "layouts/home.html");
    eleventyConfig.addLayoutAlias("posts", "layouts/posts.html");
    eleventyConfig.addLayoutAlias("search", "layouts/search.html");
    eleventyConfig.addLayoutAlias("single", "layouts/single.html");
    eleventyConfig.addLayoutAlias("splash", "layouts/splash.html");
    eleventyConfig.addLayoutAlias("tag", "layouts/tag.html");
    eleventyConfig.addLayoutAlias("tags", "layouts/tags.html");
    eleventyConfig.addLayoutAlias("gallery", "layouts/gallery");
    eleventyConfig.addLayoutAlias("drafts", "layouts/drafts");

    // Passthrough copy
    // don't use .gitignore (allows compiling sass to css into a monitored folder WITHOUT committing it to repo)
    eleventyConfig.setUseGitIgnore(false);

    //Copy CNAME
    eleventyConfig.addPassthroughCopy("src/CNAME");

    // Processing configuration
    eleventyConfig.addPassthroughCopy("src/favicon.ico");
    eleventyConfig.addPassthroughCopy("src/admin");
    eleventyConfig.addPassthroughCopy("src/assets");
    // eleventyConfig.addPassthroughCopy({ "src/_sass": "assets/css" });

    eleventyConfig.addShortcode("post_url", (collection, slug) => {
        try {
            if (collection.length < 1) {
                throw "Collection appears to be empty";
            }
            if (!Array.isArray(collection)) {
                throw "Collection is an invalid type - it must be an array!";
            }
            if (typeof slug !== "string") {
                throw "Slug is an invalid type - it must be a string!";
            }

            const found = collection.find((p) => p.fileSlug == slug);
            if (found === 0 || found === undefined) {
                throw `${slug} not found in specified collection.`;
            } else {
                return found.url;
            }
        } catch (e) {
            console.error(
                `RMCG:An error occured while searching for the url to ${slug}. Details:`,
                e
            );
        }
    });

    // Set section

    // Configure BrowserSync to serve the 404 page for missing files
    // eleventyConfig.setBrowserSyncConfig({
    //     callbacks: {
    //         ready: (_err, browserSync) => {
    //             const content_404 = fs.readFileSync("dist/404.html");

    //             browserSync.addMiddleware("*", (_req, res) => {
    //                 // render the 404 content instead of redirecting
    //                 res.write(content_404);
    //                 res.end();
    //             });
    //         },
    //     },
    // });

    eleventyConfig.setServerOptions({
        // Default values are shown:

        // Whether the live reload snippet is used
        liveReload: true,
        watch: ["dist/assets/css/main.css"],
    });

    // eleventyConfig.setBrowserSyncConfig({
    //     files: "./dist/assets/css/*.css",
    // });

    // Merge Data (https://www.11ty.dev/docs/data-deep-merge/)
    eleventyConfig.setDataDeepMerge(true);

    eleventyConfig.setFrontMatterParsingOptions({
        excerpt: true,
    });

    eleventyConfig.setLibrary("md", markdownIt().use(markdownItAnchor));

    eleventyConfig.setLiquidOptions({
        // dynamicPartials: false,
        // strictVariables: false,
        // strictFilters: false,
        jekyllInclude: true,
    });

    // Markdown Configuration
    const md = require("markdown-it")({
        html: true,
        breaks: true,
        linkify: true,
    });

    eleventyConfig.setLibrary(
        "md",
        md
            .use(require("markdown-it-attrs"))
            .use(require("markdown-it-container"), "", {
                validate: () => true,
                render: (tokens, idx) => {
                    if (tokens[idx].nesting === 1) {
                        const classList = tokens[idx].info.trim();
                        return `
`; } else { return `
`; } }, }) .use(require("markdown-it-fontawesome")) .use(require("markdown-it-footnote")) ); // override markdown-it-footnote anchor template to use a different unicode character md.renderer.rules.footnote_anchor = (tokens, idx, options, env, slf) => { var id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf); if (tokens[idx].meta.subId > 0) { id += ":" + tokens[idx].meta.subId; } /* ⇑ with escape code to prevent display as Apple Emoji on iOS */ return ( ' \u21d1\uFE0E' ); }; //for upgrade sanity //eleventyConfig.addPlugin(UpgradeHelper); // eleventyConfig.addWatchTarget("./assets/css/"); // eleventyConfig.addTransform( // "sass", // async function sassTransform(content, outputPath) { // if (outputPath?.endsWith(".css")) { // const { css } = await sassRender({ // data: content, // outputStyle: "compressed", // precision: 3, // }); // return css; // } // return content; // } // ); // eleventyConfig.addWatchTarget("dist/assets/css/*.css"); eleventyConfig.addFilter("relative_url", relativeURLALT); eleventyConfig.addFilter("absolute_url", absoluteUrl); return { templateFormats: ["html", "liquid", "md", "njk"], pathPrefix, environment: "production", // absolute_url: "https://tarasis.net/", passthroughFileCopy: true, dir: { input: "src", includes: "_includes", data: "_data", output: "dist", // input: "./", // Equivalent to Jekyll's source property // output: "./_site", // Equivalent to Jekyll's destination property }, }; }; function numberOfWords(content) { return content.split(/\s+/g).length; } function relativeURL(url, pathPrefix = undefined) { if (pathPrefix !== undefined) { // Fall back on original url filter if pathPrefix is set. return urlFilter(url, pathPrefix); } if (pathPrefix == undefined && this.page == undefined) { return urlFilter(url, ""); } // Look up the url of the current rendering page, which is accessible via // `this`. //console.log(this); // rmcg // rmcg - removed ctx from this.ctx.page.url const currentDir = this.page.url; const filteredUrl = urlFilter(url, "/"); // Make sure the index.html is expressed. const indexUrl = indexify(filteredUrl); // Check that the url doesn't specify a protocol. const u = new URL(indexUrl, "make-relative://"); if (u.protocol !== "make-relative:") { // It has a protocol, so just return the filtered URL output. return filteredUrl; } // Return the relative path, or `index.html` if it's the same as the current // page's directory. const relativePath = `${ path.relative(currentDir, u.pathname) || "index.html" }`; return relativePath; } /** * Just `{{ '/something' | url }}` will return the relative path to * `/something/index.html`. * * `{{ '/something.with.dots' | url }}` will return the relative path to * `/something.with.dots`. * * @param {string} url the URL to transform * @param {string} [pathPrefix] optional path prefix to force an absolute URL * @returns {string} resulting URL */ function relativeURLALT(url, pathPrefix = undefined) { pathPrefix = "/"; // console.log(url); // console.log(pathPrefix); // console.log(this.page); if (pathPrefix !== undefined) { // Fall back on original url filter if pathPrefix is set. return urlFilter(url, pathPrefix); } if (pathPrefix == undefined && this.page == undefined) { // console.log("dropping out"); return urlFilter(url, ""); } // Look up the url of the current rendering page, which is accessible via // `this`. console.log(this); const currentDir = this.page.url; const filteredUrl = urlFilter(url, "/"); // Make sure the index.html is expressed. const indexUrl = indexify(filteredUrl); // Check that the url doesn't specify a protocol. const u = new URL(indexUrl, "make-relative://"); if (u.protocol !== "make-relative:") { // It has a protocol, so just return the filtered URL output. return filteredUrl; } // Return the relative path, or `index.html` if it's the same as the current // page's directory. const relativePath = `${ path.relative(currentDir, u.pathname) || "index.html" }`; return relativePath; } function absoluteUrl(url) { if (url !== undefined) { return siteURL + url; } else { return siteURL; } } function groupBy(array, key) { const get = (entry) => key.split(".").reduce((acc, key) => acc[key], entry); const map = array.reduce((acc, entry) => { const value = get(entry); if (typeof acc[value] === "undefined") { acc[value] = []; } acc[value].push(entry); return acc; }, {}); return Object.keys(map).reduce( (acc, key) => [...acc, { name: key, items: map[key] }], [] ); }