| 
 | 1 | +import syntaxHighlightPlugin from '@11ty/eleventy-plugin-syntaxhighlight';  | 
 | 2 | +import markdownIt from 'markdown-it';  | 
 | 3 | +import markdownItAnchor from 'markdown-it-anchor';  | 
 | 4 | +import { parse } from 'node-html-parser';  | 
 | 5 | + | 
 | 6 | +export default function (config) {  | 
 | 7 | +  // See https://www.11ty.dev/docs/config/  | 
 | 8 | +  config.setInputDirectory('content');  | 
 | 9 | +  config.setOutputDirectory('dist');  | 
 | 10 | +  config.setDataDirectory('../_data');  | 
 | 11 | +  config.setIncludesDirectory('../_includes');  | 
 | 12 | + | 
 | 13 | +  // See https://www.11ty.dev/docs/languages/markdown/  | 
 | 14 | +  config.setLibrary(  | 
 | 15 | +    'md',  | 
 | 16 | +    markdownIt({  | 
 | 17 | +      html: true,  | 
 | 18 | +      linkify: true,  | 
 | 19 | +    }).use(markdownItAnchor, {  | 
 | 20 | +      slugify: config.getFilter('slugify'),  | 
 | 21 | +      level: [1, 2, 3, 4],  | 
 | 22 | +      permalink: markdownItAnchor.permalink.ariaHidden({  | 
 | 23 | +        placement: 'after',  | 
 | 24 | +        class: 'anchor-link',  | 
 | 25 | +        symbol: '#',  | 
 | 26 | +      }),  | 
 | 27 | +    }),  | 
 | 28 | +  );  | 
 | 29 | + | 
 | 30 | +  config.addPlugin(syntaxHighlightPlugin, {  | 
 | 31 | +    preAttributes: {  | 
 | 32 | +      class: ({ language }) => `code-block language-${language}`,  | 
 | 33 | +    },  | 
 | 34 | +  });  | 
 | 35 | + | 
 | 36 | +  config.addPassthroughCopy('css');  | 
 | 37 | +  config.addPassthroughCopy('js');  | 
 | 38 | +  config.addPassthroughCopy({ img: '/' });  | 
 | 39 | + | 
 | 40 | +  // Copy markdown files for "view as MD"  | 
 | 41 | +  config.addPassthroughCopy({ 'content/elements': 'markdown' });  | 
 | 42 | + | 
 | 43 | +  config.addCollection('elements', (api) => {  | 
 | 44 | +    return api.getFilteredByGlob('./content/elements/*.md').sort((a, b) => {  | 
 | 45 | +      return a.data.title.localeCompare(b.data.title);  | 
 | 46 | +    });  | 
 | 47 | +  });  | 
 | 48 | + | 
 | 49 | +  // Table of contents  | 
 | 50 | +  config.addFilter('toc', (content) => {  | 
 | 51 | +    const html = parse(content);  | 
 | 52 | +    const headings = html.querySelectorAll('h1, h2, h3, h4');  | 
 | 53 | +    const toc = headings.map((heading) => {  | 
 | 54 | +      // Remove anchor links  | 
 | 55 | +      heading.querySelectorAll('[aria-hidden=true]').forEach((el) => el.remove());  | 
 | 56 | + | 
 | 57 | +      const id = heading.attributes.id;  | 
 | 58 | +      const text = heading.innerText;  | 
 | 59 | +      const level = parseInt(heading.tagName.replace('H', ''), 10);  | 
 | 60 | + | 
 | 61 | +      return { id, text, level };  | 
 | 62 | +    });  | 
 | 63 | + | 
 | 64 | +    // The page title already uses an h1, so it's recommended to start with h2  | 
 | 65 | +    // in the content. If the first heading is an h2 or higher, we'll adjust the  | 
 | 66 | +    // levels to start with level 1 to avoid unnecessary indentation in the TOC.  | 
 | 67 | +    const minLevel = Math.min(...toc.map((item) => item.level));  | 
 | 68 | +    if (minLevel > 1)  | 
 | 69 | +      toc.forEach((item) => {  | 
 | 70 | +        item.level -= minLevel - 1;  | 
 | 71 | +      });  | 
 | 72 | + | 
 | 73 | +    return toc;  | 
 | 74 | +  });  | 
 | 75 | +}  | 
0 commit comments