Build Awesome / 11ty Processors

Table of Contents

Install

Install Eleventy blades
npm install @anydigital/eleventy-blades

Then choose one of the following options:

Option A. Starting 11ty from scratch?

Consider symlinking entire eleventy.config.js:

ln -s ./node_modules/@anydigital/eleventy-blades/src/eleventy.config.js

Learn more: /build-awesome-11ty/tools/#base-config

Living examples:

Option B. Adding to existing 11ty site?

Install as a plugin in your eleventy.config.js (recommended):

import eleventyBladesPlugin from "@anydigital/eleventy-blades";

export default function (eleventyConfig) {
  eleventyConfig.addPlugin(eleventyBladesPlugin, {
    mdAutoRawTags: true,
    mdAutoNl2br: true,
    autoLinkFavicons: true,
    siteData: true,
    filters: ["attr_set", "attr_concat", ...],
  });
}

Option C. Individual imports

For advanced usage, import individual components only in eleventy.config.js:

import { siteData, mdAutoRawTags, mdAutoNl2br, autoLinkFavicons, attrSetFilter, attrConcatFilter, ... } from "@anydigital/eleventy-blades";

export default function (eleventyConfig) {
  siteData(eleventyConfig);
  mdAutoRawTags(eleventyConfig);
  mdAutoNl2br(eleventyConfig);
  autoLinkFavicons(eleventyConfig);
  attrSetFilter(eleventyConfig);
  attrConcatFilter(eleventyConfig);
  ...
}

Or use a preconfigured template:

🥷 Build Awesome Starter ↗  11ty ⁺ Tailwind ⁺ Typography ⁺ Blades


Automatically adds favicon images from Google's favicon service to links that display plain URLs or domain names. This processor processes all HTML output files and adds inline favicon images next to link text that appears to be a plain URL.

Why use this? When you have links in your content that display raw URLs or domain names (like https://example.com/page), adding favicons provides a visual indicator of the external site. This processor automatically detects these plain-text URL links and enhances them with favicon images, making them more visually appealing and easier to recognize.

How it works:

  1. Scans all HTML output files for <a> tags
  2. Checks if the link text appears to be a plain URL or domain
  3. Extracts the domain from the URL
  4. Removes the domain from the link text (keeping only the path)
  5. Adds a favicon image from Google's favicon service inline with the remaining text

Example:

Before processing:

<a href="https://github.com/anydigital/eleventy-blades">https://github.com/anydigital/eleventy-blades</a>

After processing:

<a href="https://github.com/anydigital/eleventy-blades" class="whitespace-nowrap" target="_blank">
  <i><img src="https://www.google.com/s2/favicons?domain=github.com&sz=32" /></i>
  <span>/anydigital/eleventy-blades</span>
</a>

Rules:


How it works
/**
 * Check if link text looks like a plain URL or domain
 *
 * @param {string} linkText - The text content of the link
 * @param {string} domain - The domain extracted from the URL
 * @returns {boolean} True if the link text appears to be a plain URL
 */
export function isPlainUrlText(linkText, domain) {
  return linkText.trim().includes(domain) || linkText.trim().match(/^https?:\/\//) !== null;
}

/**
 * Clean link text by removing protocol, domain, and leading slash
 *
 * @param {string} linkText - The original link text
 * @param {string} domain - The domain to remove
 * @returns {string} The cleaned text
 */
export function cleanLinkText(linkText, domain) {
  const cleanedText = linkText
    .trim()
    .replace(/^https?:\/\//, "")
    .replace(/\/$/, "");
  const withoutDomain = cleanedText.replace(domain, "");
  return withoutDomain.length > 2 ? withoutDomain : cleanedText;
}

/**
 * Build HTML for a link with favicon
 *
 * @param {string} attrs - The link attributes (including href)
 * @param {string} domain - The domain for the favicon
 * @param {string} text - The text to display
 * @returns {string} The HTML string
 */
export function buildFaviconLink(attrs, domain, text) {
  let updatedAttrs = attrs;

  // Check if attrs already contains a target attribute
  if (!/target\s*=/.test(attrs)) {
    updatedAttrs = updatedAttrs + ' target="_blank"';
  }

  return `<a ${updatedAttrs} data-has-favicon><img src="https://www.google.com/s2/favicons?domain=${domain}&sz=64">${text}</a>`;
}

/**
 * Transform a single link to include a favicon
 *
 * @param {string} match - The full matched link HTML
 * @param {string} attrs - The link attributes
 * @param {string} url - The URL from the href attribute
 * @param {string} linkText - The text content of the link
 * @returns {string} The transformed link or original match if not applicable
 */
export function transformLink(match, attrs, url, linkText) {
  try {
    // Extract domain from URL
    const urlObj = new URL(url, "http://dummy.com");
    const domain = urlObj.hostname;

    // Only add favicon if link text looks like a plain URL/domain
    if (isPlainUrlText(linkText, domain)) {
      const cleanedText = cleanLinkText(linkText, domain);
      return buildFaviconLink(attrs, domain, cleanedText);
    }
    return match; // @TODO: throw?
  } catch (e) {
    // If URL parsing fails, return original match
    return match;
  }
}

/**
 * Replace all anchor links in HTML content with transformed versions
 *
 * This function searches for all anchor tags in HTML content and replaces them
 * using the provided transform function. The regex captures:
 * - Group 1: All attributes including href
 * - Group 2: The URL from the href attribute
 * - Group 3: The link text content
 *
 * @param {string} content - The HTML content to process
 * @param {Function} transformer - Function to transform each link (receives match, attrs, url, linkText)
 * @returns {string} The HTML content with transformed links
 */
export function replaceLinksInHtml(content, transformer) {
  return content.replace(/<a\s+([^>]*href=["']([^"']+)["'][^>]*)>([^<]+)<\/a>/gi, transformer);
}

/**
 * autoLinkFavicons - Add favicon images to plain text links
 *
 * This transform automatically adds favicon images from Google's favicon service
 * to links that display plain URLs or domain names. It processes all HTML output
 * files and adds inline favicon images next to the link text.
 *
 * @param {Object} eleventyConfig - The Eleventy configuration object
 */
export function autoLinkFavicons(eleventyConfig) {
  eleventyConfig.addTransform("autoLinkFavicons", function (content) {
    if (this.page.outputPath && this.page.outputPath.endsWith(".html")) {
      return replaceLinksInHtml(content, transformLink);
    }
    return content;
  });
}

mdAutoRawTags preprocessor

Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping {{, }}, {%, and %} with {% raw %} tags.

Why use this? When writing documentation or tutorials about templating in Markdown files, you often want to show Nunjucks/Liquid syntax as literal text. This preprocessor automatically escapes these special characters so they display as-is instead of being processed by the template engine.

Example:

Before mdAutoRawTags, writing this in Markdown:

### Using {{ variable }} to output variables

Would try to process {{ variable }} as a template variable. With mdAutoRawTags, it displays exactly as written.

mdAutoNl2br converter

Automatically converts \n sequences to <br> tags in Markdown content. This is particularly useful for adding line breaks inside Markdown tables where standard newlines don't work.

Why use this? Markdown tables don't support multi-line content in cells. By using \n in your content, this preprocessor will convert it to <br> tags, allowing you to display line breaks within table cells and other content.

Example:

In your Markdown file:

| Column 1               | Column 2                          |
| ---------------------- | --------------------------------- |
| Line 1\nLine 2\nLine 3 | Another cell\nWith multiple lines |

Will render as:

<td>Line 1<br />Line 2<br />Line 3</td>
<td>Another cell<br />With multiple lines</td>

Note: This processes literal \n sequences (backslash followed by 'n'), not actual newline characters. Type \n in your source files where you want line breaks.

Automatically converts \n sequences to <br> tags in Markdown content. This is particularly useful for adding line breaks inside Markdown tables where standard newlines don't work.

Why use this? Markdown tables don't support multi-line content in cells. By using \n in your content, this preprocessor will convert it to <br> tags, allowing you to display line breaks within table cells and other content.

Example:

In your Markdown file:

| Column 1               | Column 2                          |
| ---------------------- | --------------------------------- |
| Line 1\nLine 2\nLine 3 | Another cell\nWith multiple lines |

Will render as:

<td>Line 1<br />Line 2<br />Line 3</td>
<td>Another cell<br />With multiple lines</td>

Note: This processes literal \n sequences (backslash followed by 'n'), not actual newline characters. Type \n in your source files where you want line breaks. */


How it works
/**
 * Transform Nunjucks syntax in content by wrapping it with raw tags
 *
 * This function wraps Nunjucks syntax ({{, }}, {%, %}) with {% raw %} tags
 * to prevent them from being processed by the template engine.
 *
 * @param {string} content - The content to transform
 * @returns {string} The transformed content with Nunjucks syntax wrapped
 */
export function transformAutoRaw(content) {
  // This regex looks for {{, }}, {%, or %} individually and wraps them
  return content.replace(/({{|}}|{%|%})/g, "{% raw %}$1{% endraw %}");
}

/**
 * mdAutoRawTags - Forbid Nunjucks processing in Markdown files
 *
 * This preprocessor wraps Nunjucks syntax ({{, }}, {%, %}) with {% raw %} tags
 * to prevent them from being processed by the template engine in Markdown files.
 *
 * @param {Object} eleventyConfig - The Eleventy configuration object
 */
export function mdAutoRawTags(eleventyConfig) {
  eleventyConfig.addPreprocessor("mdAutoRawTags", "md", (data, content) => {
    return transformAutoRaw(content);
  });
}

/**
 * Transform \n sequences to <br> tags
 *
 * This function converts literal \n sequences (double backslash + n) to HTML <br> tags.
 * It handles both double \n\n and single \n sequences, processing double ones first.
 *
 * @param {string} content - The content to transform
 * @returns {string} The transformed content with \n converted to <br>
 */
export function transformNl2br(content) {
  // Replace double \n\n first, then single \n to avoid double conversion
  return content.replace(/\\n\\n/g, "<br>").replace(/\\n/g, "<br>");
}

/**
 * mdAutoNl2br - Auto convert \\n to <br> in markdown (especially tables)
 *
 * This function amends the markdown library to automatically convert \\n
 * to <br> tags in text content, which is particularly useful for line breaks
 * inside markdown tables where standard newlines don't work.
 *
 * @param {Object} eleventyConfig - The Eleventy configuration object
 */
export function mdAutoNl2br(eleventyConfig) {
  eleventyConfig.amendLibrary("md", (mdLib) => {
    mdLib.renderer.rules.text = (tokens, idx) => {
      return transformNl2br(tokens[idx].content);
    };
  });
}