diff --git a/_includes/recipe.njk b/_includes/recipe.njk
deleted file mode 100644
index aab5197..0000000
--- a/_includes/recipe.njk
+++ /dev/null
@@ -1,55 +0,0 @@
----
-layout: base.njk
----
-
-
-
-
- {{ content | safe }}
-
-
- {% set recipeTags = page.inputPath | extractTagsFromFile %}
-
- {% if recipeTags.length > 0 %}
-
- {% endif %}
-
- {% if not isDraft and isNewestVersion %}
-
- Permalink: /recipe/{{ recipeSlug }}/
-
- {% endif %}
-
- {% set recipeData = collections.recipesBySlug[recipeSlug] %}
- {% set nonDraftVersions = [] %}
- {% for version in recipeData.versions %}
- {% if not version.data.isDraft %}
- {% set nonDraftVersions = (nonDraftVersions.push(version), nonDraftVersions) %}
- {% endif %}
- {% endfor %}
-
- {% if nonDraftVersions.length > 1 %}
-
- Other versions:
- {% for version in nonDraftVersions %}
- {% if version.url != page.url %}
- v{{ version.data.recipeVersion }} {% if not loop.last %}, {% endif %}
- {% endif %}
- {% endfor %}
-
-
- {% endif %}
-
\ No newline at end of file
diff --git a/css/style.css b/css/style.css
index 1784a63..0aa655a 100644
--- a/css/style.css
+++ b/css/style.css
@@ -64,28 +64,6 @@ a {
color: var(--secondary-color);
}
-/* Section header with view all link */
-.section-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
-}
-
-.section-header h1 {
- margin: 1.5rem 0 1rem;
-}
-
-.view-all {
- margin-top: 1.5rem;
- font-size: 0.9rem;
- color: var(--secondary-color);
- text-decoration: none;
-}
-
-.view-all:hover {
- text-decoration: underline;
-}
-
/* Blog post list */
.post-list {
list-style: none;
diff --git a/eleventy.config.js b/eleventy.config.js
index d24ef5c..cc45552 100644
--- a/eleventy.config.js
+++ b/eleventy.config.js
@@ -208,6 +208,12 @@ module.exports = (eleventyConfig) => {
return grouped;
});
+ eleventyConfig.addCollection("contentTags", (collectionApi) => {
+ const posts = collectionApi.getFilteredByGlob("posts/**/*.md").filter(isReleased);
+
+ return [...new Set(posts.flatMap(post => getPostTags(post, md)))].sort();
+ });
+
eleventyConfig.addCollection("postsByTag", (collectionApi) => {
const posts = collectionApi.getFilteredByGlob("posts/**/*.md").filter(isReleased);
const tagMap = {};
@@ -218,7 +224,6 @@ module.exports = (eleventyConfig) => {
tagMap[tag] = {
name: tag,
posts: [post, ...(tagMap[tag]?.posts ?? [])],
- recipes: tagMap[tag]?.recipes ?? [],
}
})
});
@@ -234,109 +239,6 @@ module.exports = (eleventyConfig) => {
return tagMap;
});
- // Recipe collections
- eleventyConfig.addCollection("recipes", (collectionApi) => {
- return collectionApi.getFilteredByGlob("recipes/**/*.md")
- .filter(recipe => recipe.data.draft !== true);
- });
-
- eleventyConfig.addCollection("allRecipes", (collectionApi) => {
- return collectionApi.getFilteredByGlob("recipes/**/*.md");
- });
-
- eleventyConfig.addCollection("recipesBySlug", (collectionApi) => {
- const recipes = collectionApi.getFilteredByGlob("recipes/**/*.md");
-
- // Group recipes by slug using reduce
- const grouped = recipes.reduce((acc, recipe) => {
- const slug = recipe.data.recipeSlug;
- return {
- ...acc,
- [slug]: [...(acc[slug] || []), recipe],
- };
- }, {});
-
- // Transform grouped recipes into final structure with sorted versions and newest non-draft
- return Object.entries(grouped).reduce((acc, [slug, recipeList]) => {
- const versions = [...recipeList].sort((a, b) => a.data.recipeVersion - b.data.recipeVersion);
- const newest = [...versions].reverse().find(r => r.data.draft !== true) || null;
- return {
- ...acc,
- [slug]: { versions, newest },
- };
- }, {});
- });
-
- // Get tags from recipes (only from newest non-draft versions)
- const getRecipeTags = (recipe) => {
- const filePath = recipe.inputPath;
- try {
- const content = fs.readFileSync(filePath, 'utf-8');
- const tags = extractTags(content, md);
- return tags.map(tag => tag.toLowerCase());
- } catch (e) {
- return [];
- }
- };
-
- eleventyConfig.addCollection("contentTags", (collectionApi) => {
- const posts = collectionApi.getFilteredByGlob("posts/**/*.md").filter(isReleased);
- const recipes = collectionApi.getFilteredByGlob("recipes/**/*.md")
- .filter(r => r.data.isNewestVersion && r.data.draft !== true);
-
- const postTags = posts.flatMap(post => getPostTags(post, md));
- const recipeTags = recipes.flatMap(recipe => getRecipeTags(recipe));
-
- return [...new Set([...postTags, ...recipeTags])].sort();
- });
-
- eleventyConfig.addCollection("contentByTag", (collectionApi) => {
- const posts = collectionApi.getFilteredByGlob("posts/**/*.md").filter(isReleased);
- const recipes = collectionApi.getFilteredByGlob("recipes/**/*.md")
- .filter(r => r.data.isNewestVersion && r.data.draft !== true);
-
- const sortByDate = (a, b) => {
- const aDate = a.data.createdAt || a.date;
- const bDate = b.data.createdAt || b.date;
- return aDate - bDate;
- };
-
- // Build tag map from posts
- const postTagMap = posts.reduce((acc, post) => {
- const tags = getPostTags(post, md);
- return tags.reduce((innerAcc, tag) => ({
- ...innerAcc,
- [tag]: {
- name: tag,
- posts: [...(innerAcc[tag]?.posts || []), post],
- recipes: innerAcc[tag]?.recipes || [],
- },
- }), acc);
- }, {});
-
- // Merge recipe tags into the tag map
- const tagMap = recipes.reduce((acc, recipe) => {
- const tags = getRecipeTags(recipe);
- return tags.reduce((innerAcc, tag) => ({
- ...innerAcc,
- [tag]: {
- name: tag,
- posts: innerAcc[tag]?.posts || [],
- recipes: [...(innerAcc[tag]?.recipes || []), recipe],
- },
- }), acc);
- }, postTagMap);
-
- // Return with sorted posts
- return Object.entries(tagMap).reduce((acc, [tag, tagData]) => ({
- ...acc,
- [tag]: {
- ...tagData,
- posts: [...tagData.posts].sort(sortByDate),
- },
- }), {});
- });
-
// Cache busting filter: returns hashed filename
eleventyConfig.addFilter("fileHash", (file, dir = "css") => {
const hash = getFileHash(file, dir);
@@ -397,6 +299,7 @@ module.exports = (eleventyConfig) => {
return {
dir: {
+ input: ".",
includes: "_includes",
output: "_site"
},
diff --git a/index.njk b/index.njk
index 71793b8..bc952c7 100644
--- a/index.njk
+++ b/index.njk
@@ -8,17 +8,10 @@ description: Welcome to my website! I write about tech, politics, food, and hobb
{{ description }}
-{% if collections.posts.length > 0 %}
-
+Blog Posts
-{% endif %}
-{% set recipesList = [] %}
-{% for slug, recipeData in collections.recipesBySlug %}
- {% if recipeData.newest %}
- {% set recipesList = recipesList.concat([{slug: slug, data: recipeData}]) %}
- {% endif %}
-{% endfor %}
-
-{% if recipesList.length > 0 %}
-
-
-
+{% if collections.posts.length == 0 %}
+No blog posts yet.
{% endif %}
\ No newline at end of file
diff --git a/posts-index.njk b/posts-index.njk
deleted file mode 100644
index abb3d19..0000000
--- a/posts-index.njk
+++ /dev/null
@@ -1,26 +0,0 @@
----
-layout: base.njk
-title: All Blog Posts
-description: All blog posts on Volpe.
-permalink: /posts/
----
-
-Blog Posts
-
-{% if collections.posts.length > 0 %}
-
-{% else %}
-No blog posts yet.
-{% endif %}
\ No newline at end of file
diff --git a/posts/posts.11tydata.js b/posts/posts.11tydata.js
index 4d8d3bd..fcec690 100644
--- a/posts/posts.11tydata.js
+++ b/posts/posts.11tydata.js
@@ -42,13 +42,6 @@ const getTitleFromFilename = (filePath) => {
}
const getFileCreatedTime = (filePath) => {
- // Try git first for accurate cross-system creation time
- const gitTime = getGitCreatedTime(filePath);
- if (gitTime) {
- return gitTime;
- }
-
- // Fall back to filesystem for untracked files
try {
const stats = fs.statSync(filePath);
const time = stats.birthtime ?? stats.mtime;
diff --git a/recipe.njk b/recipe.njk
deleted file mode 100644
index 93ce1a2..0000000
--- a/recipe.njk
+++ /dev/null
@@ -1,61 +0,0 @@
----
-pagination:
- data: collections.recipesBySlug
- size: 1
- alias: slugData
- resolve: keys
-permalink: /recipe/{{ slugData }}/
-layout: base.njk
-excludeFromSitemap: true
-eleventyComputed:
- title: "{{ collections.recipesBySlug[slugData].newest.data.title }}"
----
-
-{% set recipeData = collections.recipesBySlug[slugData] %}
-{% set recipe = recipeData.newest %}
-
-{% if recipe %}
-
-
-
-
- {{ recipe.content | safe }}
-
-
- {% set recipeTags = recipe.inputPath | extractTagsFromFile %}
-
- {% if recipeTags.length > 0 %}
-
- {% endif %}
-
- {% set nonDraftVersions = [] %}
- {% for version in recipeData.versions %}
- {% if not version.data.isDraft %}
- {% set nonDraftVersions = (nonDraftVersions.push(version), nonDraftVersions) %}
- {% endif %}
- {% endfor %}
-
- {% if nonDraftVersions.length > 1 %}
-
- Other versions:
- {% for version in nonDraftVersions %}
- {% if not version.data.isNewestVersion %}
- v{{ version.data.recipeVersion }} {% if not loop.last %}, {% endif %}
- {% endif %}
- {% endfor %}
-
-
- {% endif %}
-
-{% else %}
-No published recipe found for this slug.
-{% endif %}
\ No newline at end of file
diff --git a/recipes-index.njk b/recipes-index.njk
deleted file mode 100644
index b129f8c..0000000
--- a/recipes-index.njk
+++ /dev/null
@@ -1,34 +0,0 @@
----
-layout: base.njk
-title: All Recipes
-description: All recipes on Volpe.
-permalink: /recipes/
----
-
-Recipes
-
-{% set hasRecipes = false %}
-{% for slug, recipeData in collections.recipesBySlug %}
- {% if recipeData.newest %}
- {% set hasRecipes = true %}
- {% endif %}
-{% endfor %}
-
-{% if hasRecipes %}
-
-{% else %}
-No recipes yet.
-{% endif %}
\ No newline at end of file
diff --git a/recipes/recipes.11tydata.js b/recipes/recipes.11tydata.js
deleted file mode 100644
index 9c6a9d2..0000000
--- a/recipes/recipes.11tydata.js
+++ /dev/null
@@ -1,154 +0,0 @@
-const fs = require('fs')
-const path = require("path");
-const { execSync } = require("child_process");
-
-const getSlugFromPath = (filePath) => {
- // Normalize the path - remove leading ./ if present
- const normalizedPath = filePath.startsWith('./') ? filePath.slice(2) : filePath;
-
- // For top-level files: recipes/foo.md -> slug is "foo"
- // For nested folders: recipes/bar/v1.md -> slug is "bar"
- const parts = normalizedPath.split(path.sep);
-
- // parts[0] should be 'recipes', parts[1] is the key part
- if (parts.length >= 2 && parts[0] === 'recipes') {
- const secondPart = parts[1];
- // If it's a .md file at the top level (recipes/foo.md), strip the extension
- if (secondPart.endsWith('.md')) {
- return path.basename(secondPart, '.md');
- }
- // Otherwise it's a folder name (recipes/bar/v1.md -> bar)
- return secondPart;
- }
- return path.basename(filePath, '.md');
-}
-
-const getTitleFromSlug = (slug) => {
- return slug
- .split('-')
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
- .join(' ');
-}
-
-const getGitCreatedTime = (filePath) => {
- try {
- const result = execSync(
- `git log --diff-filter=A --follow --format=%aI -- "${filePath}" | tail -1`,
- { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
- ).trim();
- if (result) {
- return new Date(result).getTime();
- }
- } catch (e) {
- // Git command failed, fall through to filesystem
- }
- return null;
-}
-
-const getFileCreatedTime = (filePath) => {
- // Try git first for accurate cross-system creation time
- const gitTime = getGitCreatedTime(filePath);
- if (gitTime) {
- return gitTime;
- }
-
- // Fall back to filesystem for untracked files
- try {
- const stats = fs.statSync(filePath);
- const time = stats.birthtime ?? stats.mtime;
- return time.getTime();
- } catch (e) {
- return Date.now();
- }
-}
-
-const getVersion = (filePath) => {
- const dirName = path.dirname(filePath)
-
- // Top-level files (directly in recipes/) always have version 0
- if (dirName === 'recipes' || dirName === './recipes') {
- return 0
- }
-
- const files = fs.readdirSync(dirName).filter(f => f.endsWith('.md'))
-
- const filesWithDates = files
- .map((file) => {
- const fullPath = path.join(dirName, file);
- return {
- file: fullPath,
- createdAt: getFileCreatedTime(fullPath),
- }
- })
- .sort((a, b) => a.createdAt - b.createdAt) // oldest first (version 0)
-
- const normalizedFilePath = filePath.startsWith('./') ? filePath.slice(2) : filePath;
- const version = filesWithDates.findIndex(({ file }) => {
- const normalizedFile = file.startsWith('./') ? file.slice(2) : file;
- return normalizedFile === normalizedFilePath;
- });
-
- return version === -1 ? 0 : version;
-}
-
-const isNewestVersion = (filePath) => {
- const dirName = path.dirname(filePath)
-
- // Top-level files are always the "newest" (only version)
- if (dirName === 'recipes' || dirName === './recipes') {
- return true
- }
-
- const files = fs.readdirSync(dirName).filter(f => f.endsWith('.md'))
-
- const filesWithDates = files
- .map((file) => {
- const fullPath = path.join(dirName, file);
- return {
- file: fullPath,
- createdAt: getFileCreatedTime(fullPath),
- }
- })
- .sort((a, b) => b.createdAt - a.createdAt) // newest first
-
- const normalizedFilePath = filePath.startsWith('./') ? filePath.slice(2) : filePath;
- const newestFile = filesWithDates[0]?.file;
- const normalizedNewest = newestFile?.startsWith('./') ? newestFile.slice(2) : newestFile;
-
- return normalizedFilePath === normalizedNewest;
-}
-
-module.exports = {
- layout: "recipe.njk",
- tags: ["recipe"],
- eleventyComputed: {
- recipeSlug: (data) => {
- return getSlugFromPath(data.page.inputPath);
- },
- title: (data) => {
- if (data.title && data.title !== data.page.fileSlug) {
- return data.title;
- }
- const slug = getSlugFromPath(data.page.inputPath);
- return getTitleFromSlug(slug);
- },
- recipeVersion: (data) => {
- return getVersion(data.page.inputPath);
- },
- isNewestVersion: (data) => {
- // Draft recipes are never considered "newest" for redirect purposes
- if (data.draft === true) {
- return false;
- }
- return isNewestVersion(data.page.inputPath);
- },
- isDraft: (data) => {
- return data.draft === true;
- },
- permalink: (data) => {
- const slug = getSlugFromPath(data.page.inputPath);
- const version = getVersion(data.page.inputPath);
- return `/recipe/${slug}/${version}/`;
- }
- }
-}
\ No newline at end of file
diff --git a/tags.njk b/tags.njk
index fad82f2..e0a18b4 100644
--- a/tags.njk
+++ b/tags.njk
@@ -1,6 +1,6 @@
---
pagination:
- data: collections.contentByTag
+ data: collections.postsByTag
size: 1
alias: tag
addAllPagesToCollections: true
@@ -8,14 +8,10 @@ permalink: /tags/{{ tag }}/
layout: base.njk
---
-Content tagged "#{{ collections.contentByTag[tag].name }}"
+Posts tagged "#{{ collections.postsByTag[tag].name }}"
-{% set tagData = collections.contentByTag[tag] %}
-
-{% if tagData.posts.length > 0 %}
-Posts
- {% for post in tagData.posts %}
+ {% for post in collections.postsByTag[tag].posts %}
{{ post.data.title }}
@@ -27,22 +23,5 @@ layout: base.njk
{% endfor %}
-{% endif %}
-{% if tagData.recipes.length > 0 %}
-Recipes
-
-{% endif %}
-
-← All tags
\ No newline at end of file
+� All tags
\ No newline at end of file