feat: created basic blog layout
This commit is contained in:
parent
42fd071301
commit
0d41db36ef
13 changed files with 935 additions and 3 deletions
210
eleventy.config.js
Normal file
210
eleventy.config.js
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
const markdownIt = require("markdown-it");
|
||||
const markdownItContainer = require("markdown-it-container");
|
||||
const markdownItFootnote = require("markdown-it-footnote");
|
||||
const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
|
||||
const fs = require("fs");
|
||||
|
||||
const tagPattern = /(?<=^|\s)#([a-zA-Z][a-zA-Z0-9_]*)(?![a-zA-Z0-9_-])/g;
|
||||
|
||||
// TODO: is there any reasonable way to make this use real markdown parsing because right now this is sketchy
|
||||
const extractTags = (content) => {
|
||||
if (!content) return [];
|
||||
const matches = content.match(tagPattern);
|
||||
if (!matches) return [];
|
||||
const tags = [...new Set(matches.map(m => m.slice(1)))];
|
||||
return tags;
|
||||
}
|
||||
|
||||
const getPostTags = (post) => {
|
||||
const filePath = post.inputPath;
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const tags = extractTags(content);
|
||||
return tags.map(tag => {
|
||||
const normalizedTag = tag.toLowerCase();
|
||||
return normalizedTag
|
||||
});
|
||||
} catch (e) {
|
||||
// Skip if file can't be read
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
const isReleased = (post) => {
|
||||
return post.data.unreleased !== true;
|
||||
}
|
||||
|
||||
const markdownItHashtag = (md) => {
|
||||
const hashtagRegex = /^#([a-zA-Z][a-zA-Z0-9_]*)(?![a-zA-Z0-9_-])/;
|
||||
|
||||
md.inline.ruler.push('hashtag', function(state, silent) {
|
||||
const pos = state.pos;
|
||||
const ch = state.src.charCodeAt(pos);
|
||||
|
||||
if (ch !== '#') return false;
|
||||
|
||||
if (pos > 0) {
|
||||
const prevCh = state.src.charCodeAt(pos - 1);
|
||||
if (prevCh !== ' ' && prevCh !== '\t' && prevCh !== '\n' && prevCh !== '\r') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const match = state.src.slice(pos).match(hashtagRegex);
|
||||
if (!match) return false;
|
||||
|
||||
if (!silent) {
|
||||
const token = state.push('hashtag', 'a', 0);
|
||||
token.content = match[1];
|
||||
token.markup = '#';
|
||||
}
|
||||
|
||||
state.pos += match[0].length;
|
||||
return true;
|
||||
});
|
||||
|
||||
md.renderer.rules.hashtag = function(tokens, idx) {
|
||||
const tagName = tokens[idx].content;
|
||||
const slug = tagName.toLowerCase();
|
||||
return `<a href="/tags/${slug}/" class="inline-tag">#${md.utils.escapeHtml(tagName)}</a>`;
|
||||
};
|
||||
}
|
||||
|
||||
const md = markdownIt({
|
||||
html: true,
|
||||
breaks: true,
|
||||
linkify: true
|
||||
});
|
||||
|
||||
md.use(markdownItContainer, 'details', {
|
||||
validate: function (params) {
|
||||
return params.trim().match(/^(.*)$/);
|
||||
},
|
||||
render: function (tokens, idx) {
|
||||
const m = tokens[idx].info.trim().match(/^(.*)$/);
|
||||
if (tokens[idx].nesting === 1) {
|
||||
const title = md.utils.escapeHtml(m[1]);
|
||||
return `<details class="expandable">\n<summary>${title}</summary>\n`;
|
||||
} else {
|
||||
return '</details>\n';
|
||||
}
|
||||
}
|
||||
});
|
||||
md.use(markdownItFootnote);
|
||||
md.use(markdownItHashtag);
|
||||
|
||||
module.exports = (eleventyConfig) => {
|
||||
eleventyConfig.addPlugin(syntaxHighlight);
|
||||
|
||||
eleventyConfig.addFilter("extractTags", extractTags);
|
||||
eleventyConfig.addFilter("extractTagsFromFile", (filePath) => {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
return extractTags(content);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
eleventyConfig.setLibrary("md", md);
|
||||
|
||||
eleventyConfig.addFilter("dateFormat", (date) => {
|
||||
const d = new Date(date);
|
||||
return d.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC'
|
||||
});
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter("isoDate", (date) => {
|
||||
const d = new Date(date);
|
||||
return d.toISOString().split('T')[0];
|
||||
});
|
||||
|
||||
eleventyConfig.addCollection("posts", (collectionApi) => {
|
||||
return collectionApi.getFilteredByGlob("posts/**/*.md")
|
||||
.filter(isReleased)
|
||||
.sort((a, b) => {
|
||||
const aDate = a.data.createdAt || a.date;
|
||||
const bDate = b.data.createdAt || b.date;
|
||||
return aDate - bDate;
|
||||
});
|
||||
});
|
||||
|
||||
eleventyConfig.addCollection("allPosts", (collectionApi) => {
|
||||
return collectionApi.getFilteredByGlob("posts/**/*.md").sort((a, b) => {
|
||||
const aDate = a.data.createdAt || a.date;
|
||||
const bDate = b.data.createdAt || b.date;
|
||||
return aDate - bDate;
|
||||
});
|
||||
});
|
||||
|
||||
eleventyConfig.addCollection("postsBySlug", (collectionApi) => {
|
||||
const posts = collectionApi.getFilteredByGlob("posts/**/*.md").filter(isReleased);
|
||||
const slugify = eleventyConfig.getFilter("slugify");
|
||||
const grouped = {};
|
||||
|
||||
posts.forEach(post => {
|
||||
const slug = slugify(post.data.title);
|
||||
if (!grouped[slug]) {
|
||||
grouped[slug] = [];
|
||||
}
|
||||
grouped[slug].push(post);
|
||||
});
|
||||
|
||||
Object.values(grouped).forEach(postList => {
|
||||
postList.sort((a, b) => {
|
||||
const aDate = a.data.createdAt || a.date;
|
||||
const bDate = b.data.createdAt || b.date;
|
||||
return aDate - bDate;
|
||||
});
|
||||
});
|
||||
|
||||
return grouped;
|
||||
});
|
||||
|
||||
eleventyConfig.addCollection("contentTags", (collectionApi) => {
|
||||
const posts = collectionApi.getFilteredByGlob("posts/**/*.md").filter(isReleased);
|
||||
|
||||
return [...new Set(posts.flatMap(getPostTags))].sort();
|
||||
});
|
||||
|
||||
eleventyConfig.addCollection("postsByTag", (collectionApi) => {
|
||||
const posts = collectionApi.getFilteredByGlob("posts/**/*.md").filter(isReleased);
|
||||
const tagMap = {};
|
||||
|
||||
posts.forEach(post => {
|
||||
const tags = getPostTags(post)
|
||||
tags.forEach((tag) => {
|
||||
tagMap[tag] = {
|
||||
name: tag,
|
||||
posts: [post, ...(tagMap[tag]?.posts ?? [])],
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Object.values(tagMap).forEach(tagData => {
|
||||
tagData.posts.sort((a, b) => {
|
||||
const aDate = a.data.createdAt || a.date;
|
||||
const bDate = b.data.createdAt || b.date;
|
||||
return aDate - bDate;
|
||||
});
|
||||
});
|
||||
|
||||
return tagMap;
|
||||
});
|
||||
|
||||
eleventyConfig.addPassthroughCopy("css");
|
||||
|
||||
return {
|
||||
dir: {
|
||||
input: ".",
|
||||
includes: "_includes",
|
||||
output: "_site"
|
||||
},
|
||||
markdownTemplateEngine: "njk",
|
||||
htmlTemplateEngine: "njk"
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue