feat: allow blog posts to be organized by subdirectories

Blog posts can now be structured into subdirectories, which determine their slugs.

Example:
- `/src/data/blog/2025/testing.md` → `/posts/2025/testing`

Directories prefixed with `_` will be excluded from the slug.

Example:
- `/src/data/blog/_examples/tailwind-typography.md` → `/posts/tailwind-typography`

Closes #470, #366
This commit is contained in:
satnaing
2025-03-16 20:09:03 +07:00
committed by Sat Naing
parent 67b9536cce
commit feaf5e122a
19 changed files with 64 additions and 39 deletions
+36
View File
@@ -0,0 +1,36 @@
import { BLOG_PATH } from "@/content.config";
import { slugifyStr } from "./slugify";
/**
* Get full path of a blog post
* @param id - id of the blog post (aka slug)
* @param filePath - the blog post full file location
* @param includeBase - whether to include `/posts` in return value
* @returns blog post path
*/
export function getPath(
id: string,
filePath: string | undefined,
includeBase = true
) {
const pathSegments = filePath
?.replace(BLOG_PATH, "")
.split("/")
.filter(path => path !== "") // remove empty string in the segments ["", "other-path"] <- empty string will be removed
.filter(path => !path.startsWith("_")) // exclude directories start with underscore "_"
.slice(0, -1) // remove the last segment_ file name_ since it's unnecessary
.map(segment => slugifyStr(segment)); // slugify each segment path
const basePath = includeBase ? "/posts" : "";
// Making sure `id` does not contain the directory
const blogId = id.split("/");
const slug = blogId.length > 0 ? blogId.slice(-1) : blogId;
// If not inside the sub-dir, simply return the file path
if (!pathSegments || pathSegments.length < 1) {
return [basePath, slug].join("/");
}
return [basePath, ...pathSegments, slug].join("/");
}