diff --git a/src/components/Card.astro b/src/components/Card.astro
index 572eab8..59bc65a 100644
--- a/src/components/Card.astro
+++ b/src/components/Card.astro
@@ -1,17 +1,16 @@
---
import { slugifyStr } from "@/utils/slugify";
import type { CollectionEntry } from "astro:content";
+import { getPath } from "@/utils/getPath";
import Datetime from "./Datetime.astro";
-export interface Props {
+export interface Props extends CollectionEntry<"blog"> {
variant?: "h2" | "h3";
- href?: string;
- frontmatter: CollectionEntry<"blog">["data"];
}
-const { href, frontmatter, variant = "h2" } = Astro.props;
+const { variant = "h2", data, id, filePath } = Astro.props;
-const { title, pubDatetime, modDatetime, description } = frontmatter;
+const { title, pubDatetime, modDatetime, description } = data;
const headerProps = {
style: { viewTransitionName: slugifyStr(title) },
@@ -21,7 +20,7 @@ const headerProps = {
{
diff --git a/src/config.ts b/src/config.ts
index 4731a27..9c86a05 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -12,7 +12,7 @@ export const SITE = {
showArchives: true,
showBackButton: true, // show back button in post detail
editPost: {
- url: "https://github.com/satnaing/astro-paper/edit/main/src/content/blog",
+ url: "https://github.com/satnaing/astro-paper/edit/main/src/data/blog",
text: "Suggest Changes",
appendFilePath: true,
},
diff --git a/src/content.config.ts b/src/content.config.ts
index cfc3501..d5ebadc 100644
--- a/src/content.config.ts
+++ b/src/content.config.ts
@@ -2,8 +2,10 @@ import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";
import { SITE } from "@/config";
+export const BLOG_PATH = "src/data/blog";
+
const blog = defineCollection({
- loader: glob({ pattern: "**/[^_]*.md", base: "./src/data/blog" }),
+ loader: glob({ pattern: "**/[^_]*.md", base: `./${BLOG_PATH}` }),
schema: ({ image }) =>
z.object({
author: z.string().default(SITE.author),
diff --git a/src/data/blog/example-draft-post.md b/src/data/blog/_examples/example-draft-post.md
similarity index 100%
rename from src/data/blog/example-draft-post.md
rename to src/data/blog/_examples/example-draft-post.md
diff --git a/src/data/blog/tailwind-typography.md b/src/data/blog/_examples/tailwind-typography.md
similarity index 100%
rename from src/data/blog/tailwind-typography.md
rename to src/data/blog/_examples/tailwind-typography.md
diff --git a/src/data/blog/terminal-development.md b/src/data/blog/_examples/terminal-development.md
similarity index 100%
rename from src/data/blog/terminal-development.md
rename to src/data/blog/_examples/terminal-development.md
diff --git a/src/data/blog/astro-paper-2.md b/src/data/blog/releases/astro-paper-2.md
similarity index 100%
rename from src/data/blog/astro-paper-2.md
rename to src/data/blog/releases/astro-paper-2.md
diff --git a/src/data/blog/astro-paper-3.md b/src/data/blog/releases/astro-paper-3.md
similarity index 100%
rename from src/data/blog/astro-paper-3.md
rename to src/data/blog/releases/astro-paper-3.md
diff --git a/src/data/blog/astro-paper-4.md b/src/data/blog/releases/astro-paper-4.md
similarity index 99%
rename from src/data/blog/astro-paper-4.md
rename to src/data/blog/releases/astro-paper-4.md
index 68b11d2..89d40ef 100644
--- a/src/data/blog/astro-paper-4.md
+++ b/src/data/blog/releases/astro-paper-4.md
@@ -4,7 +4,7 @@ pubDatetime: 2024-01-04T09:30:41.816Z
title: AstroPaper 4.0
slug: "astro-paper-v4"
featured: false
-ogImage: ../../assets/images/AstroPaper-v4.png
+ogImage: ../../../assets/images/AstroPaper-v4.png
tags:
- release
description: "AstroPaper v4: ensuring a smoother and more feature-rich blogging experience."
diff --git a/src/data/blog/astro-paper-5.md b/src/data/blog/releases/astro-paper-5.md
similarity index 98%
rename from src/data/blog/astro-paper-5.md
rename to src/data/blog/releases/astro-paper-5.md
index 309a8df..89ec3df 100644
--- a/src/data/blog/astro-paper-5.md
+++ b/src/data/blog/releases/astro-paper-5.md
@@ -3,7 +3,7 @@ pubDatetime: 2025-03-08T08:18:19.693Z
title: AstroPaper 5.0
slug: astro-paper-v5
featured: true
-ogImage: ../../assets/images/AstroPaper-v5.png
+ogImage: ../../../assets/images/AstroPaper-v5.png
tags:
- release
description: "AstroPaper v5: keep the clean look, updates under the hood."
diff --git a/src/layouts/PostDetails.astro b/src/layouts/PostDetails.astro
index 720787b..da27ea6 100644
--- a/src/layouts/PostDetails.astro
+++ b/src/layouts/PostDetails.astro
@@ -8,6 +8,7 @@ import Datetime from "@/components/Datetime.astro";
import EditPost from "@/components/EditPost.astro";
import ShareLinks from "@/components/ShareLinks.astro";
import BackButton from "@/components/BackButton.astro";
+import { getPath } from "@/utils/getPath";
import { slugifyStr } from "@/utils/slugify";
import IconChevronLeft from "@/assets/icons/IconChevronLeft.svg";
import IconChevronRight from "@/assets/icons/IconChevronRight.svg";
@@ -45,7 +46,7 @@ if (typeof initOgImage === "string") {
// Use dynamic OG image if enabled and no remote|local ogImage
if (!ogImageUrl && SITE.dynamicOgImage) {
- ogImageUrl = `/posts/${slugifyStr(title)}/index.png`;
+ ogImageUrl = `${getPath(post.id, post.filePath)}/index.png`;
}
// Resolve OG image URL (or fallback to SITE.ogImage / default `og.png`)
diff --git a/src/pages/archives/index.astro b/src/pages/archives/index.astro
index 72369e6..030d671 100644
--- a/src/pages/archives/index.astro
+++ b/src/pages/archives/index.astro
@@ -69,8 +69,8 @@ const months = [
new Date(a.data.pubDatetime).getTime() / 1000
)
)
- .map(({ data, id }) => (
-
+ .map(data => (
+
))}
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 9df2c32..45fc8e0 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -76,8 +76,8 @@ const recentPosts = sortedPosts.filter(({ data }) => !data.featured);
Featured
- {featuredPosts.map(({ data, id }) => (
-
+ {featuredPosts.map(data => (
+
))}
@@ -92,14 +92,8 @@ const recentPosts = sortedPosts.filter(({ data }) => !data.featured);
Recent Posts
{recentPosts.map(
- ({ data, id }, index) =>
- index < SITE.postPerIndex && (
-
- )
+ (data, index) =>
+ index < SITE.postPerIndex &&
)}
diff --git a/src/pages/posts/[...page].astro b/src/pages/posts/[...page].astro
index 026673e..2c554aa 100644
--- a/src/pages/posts/[...page].astro
+++ b/src/pages/posts/[...page].astro
@@ -22,11 +22,7 @@ const { page } = Astro.props;
- {
- page.data.map(({ data, id }) => (
-
- ))
- }
+ {page.data.map(data => )}
diff --git a/src/pages/posts/[slug]/index.astro b/src/pages/posts/[...slug]/index.astro
similarity index 85%
rename from src/pages/posts/[slug]/index.astro
rename to src/pages/posts/[...slug]/index.astro
index 2ee0f72..340be11 100644
--- a/src/pages/posts/[slug]/index.astro
+++ b/src/pages/posts/[...slug]/index.astro
@@ -2,6 +2,7 @@
import { type CollectionEntry, getCollection } from "astro:content";
import PostDetails from "@/layouts/PostDetails.astro";
import getSortedPosts from "@/utils/getSortedPosts";
+import { getPath } from "@/utils/getPath";
export interface Props {
post: CollectionEntry<"blog">;
@@ -9,9 +10,8 @@ export interface Props {
export async function getStaticPaths() {
const posts = await getCollection("blog", ({ data }) => !data.draft);
-
const postResult = posts.map(post => ({
- params: { slug: post.id },
+ params: { slug: getPath(post.id, post.filePath, false) },
props: { post },
}));
diff --git a/src/pages/posts/[slug]/index.png.ts b/src/pages/posts/[...slug]/index.png.ts
similarity index 88%
rename from src/pages/posts/[slug]/index.png.ts
rename to src/pages/posts/[...slug]/index.png.ts
index 396cfe6..3295ecb 100644
--- a/src/pages/posts/[slug]/index.png.ts
+++ b/src/pages/posts/[...slug]/index.png.ts
@@ -1,7 +1,7 @@
import type { APIRoute } from "astro";
import { getCollection, type CollectionEntry } from "astro:content";
+import { getPath } from "@/utils/getPath";
import { generateOgImageForPost } from "@/utils/generateOgImages";
-import { slugifyStr } from "@/utils/slugify";
import { SITE } from "@/config";
export async function getStaticPaths() {
@@ -14,7 +14,7 @@ export async function getStaticPaths() {
);
return posts.map(post => ({
- params: { slug: slugifyStr(post.data.title) },
+ params: { slug: getPath(post.id, post.filePath, false) },
props: post,
}));
}
diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts
index 3322a07..642e4f2 100644
--- a/src/pages/rss.xml.ts
+++ b/src/pages/rss.xml.ts
@@ -1,5 +1,6 @@
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
+import { getPath } from "@/utils/getPath";
import getSortedPosts from "@/utils/getSortedPosts";
import { SITE } from "@/config";
@@ -10,8 +11,8 @@ export async function GET() {
title: SITE.title,
description: SITE.desc,
site: SITE.website,
- items: sortedPosts.map(({ data, id }) => ({
- link: `posts/${id}/`,
+ items: sortedPosts.map(({ data, id, filePath }) => ({
+ link: getPath(id, filePath),
title: data.title,
description: data.description,
pubDate: new Date(data.modDatetime ?? data.pubDatetime),
diff --git a/src/pages/tags/[tag]/[...page].astro b/src/pages/tags/[tag]/[...page].astro
index fcb8a4a..cb9dec2 100644
--- a/src/pages/tags/[tag]/[...page].astro
+++ b/src/pages/tags/[tag]/[...page].astro
@@ -40,11 +40,7 @@ const { page, tagName } = Astro.props;
>
{`Tag:${tag}`}
- {
- page.data.map(({ data, id }) => (
-
- ))
- }
+ {page.data.map(data => )}
diff --git a/src/utils/getPath.ts b/src/utils/getPath.ts
new file mode 100644
index 0000000..057b612
--- /dev/null
+++ b/src/utils/getPath.ts
@@ -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("/");
+}