- Update AGENTS.md and .agents/rules/blog-system.md with strict TDD mandate - Extract Eleventy custom filters into modular src/_filters/index.js for unit testing - Add npm test script using Node.js native test runner (node:test) - Create test/site-config.test.js for environment validation and fatal error assertions - Create test/filters.test.js for adversarial filter edge cases (dates, limits, reading time, headings) - Create test/frontmatter-schema.test.js enforcing post metadata contracts - Create test/design-rules.test.js enforcing strict anti-patterns (no glassmorphism, no shadows, max 4px radius) - Create test/build-integrity.test.js checking static output, zero 404 links, and TOC anchor targets
77 lines
2.1 KiB
JavaScript
77 lines
2.1 KiB
JavaScript
import syntaxHighlight from "@11ty/eleventy-plugin-syntaxhighlight";
|
|
import markdownIt from "markdown-it";
|
|
import markdownItAnchor from "markdown-it-anchor";
|
|
import { filters } from "./src/_filters/index.js";
|
|
|
|
export default function (eleventyConfig) {
|
|
// 1. Static Asset Passthrough Copy
|
|
eleventyConfig.addPassthroughCopy({ "src/assets": "assets" });
|
|
|
|
// 2. Syntax Highlighting Plugin
|
|
eleventyConfig.addPlugin(syntaxHighlight);
|
|
|
|
// 3. Markdown-It Library Customization
|
|
const markdownOptions = {
|
|
html: true,
|
|
breaks: false,
|
|
linkify: true
|
|
};
|
|
|
|
const markdownLib = markdownIt(markdownOptions).use(markdownItAnchor, {
|
|
permalink: markdownItAnchor.permalink.headerLink({
|
|
safariReaderFix: true
|
|
}),
|
|
slugify: (s) =>
|
|
s
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9 -]/g, "")
|
|
.replace(/\s+/g, "-")
|
|
.replace(/-+/g, "-")
|
|
});
|
|
|
|
eleventyConfig.setLibrary("md", markdownLib);
|
|
|
|
// 4. Custom Filters (imported from modular src/_filters/index.js)
|
|
for (const [name, fn] of Object.entries(filters)) {
|
|
eleventyConfig.addFilter(name, fn);
|
|
}
|
|
|
|
// 5. Custom Collections
|
|
// Chronological blog posts collection
|
|
eleventyConfig.addCollection("posts", (collectionApi) => {
|
|
return collectionApi
|
|
.getFilteredByGlob("src/posts/*.md")
|
|
.filter((post) => !post.data.draft)
|
|
.sort((a, b) => b.date - a.date);
|
|
});
|
|
|
|
// Dynamic Tag List Collection across all posts
|
|
eleventyConfig.addCollection("tagList", (collectionApi) => {
|
|
const tagSet = new Set();
|
|
collectionApi.getFilteredByGlob("src/posts/*.md").forEach((item) => {
|
|
if ("tags" in item.data) {
|
|
let tags = item.data.tags;
|
|
if (typeof tags === "string") tags = [tags];
|
|
for (const tag of tags) {
|
|
const cleanTag = tag.replace(/^#/, "");
|
|
tagSet.add(cleanTag);
|
|
}
|
|
}
|
|
});
|
|
return [...tagSet].sort();
|
|
});
|
|
|
|
return {
|
|
dir: {
|
|
input: "src",
|
|
includes: "_includes",
|
|
data: "_data",
|
|
output: "_site"
|
|
},
|
|
templateFormats: ["njk", "md", "html"],
|
|
htmlTemplateEngine: "njk",
|
|
markdownTemplateEngine: "njk"
|
|
};
|
|
}
|