diff --git a/src/config.mjs b/src/config.mjs
index 5dcea15..46a0298 100644
--- a/src/config.mjs
+++ b/src/config.mjs
@@ -1,12 +1,30 @@
export const SITE = {
name: "AstroWind",
+
domain: "https://astrowind.vercel.app",
+ baseUrl: "/",
title: "AstroWind — Your website with Astro + Tailwind CSS",
description: "🚀 AstroWind is a free and ready to start template to make your website using Astro and Tailwind CSS.",
- postsPerPage: 6,
-
googleAnalyticsId: false, // or "G-XXXXXXXXXX",
googleSiteVerificationId: "orcPxI47GSa-cRvY11tUe6iGg2IO_RPvnA1q95iEM3M",
};
+
+export const BLOG = {
+ disabled: false,
+ slug: "blog",
+
+ postsWithoutBlogSlug: true,
+ postsPerPage: 6,
+
+ category: {
+ disabled: false,
+ slug: "",
+ },
+
+ tag: {
+ disabled: false,
+ slug: "tag",
+ },
+};
diff --git a/src/data/posts/astrowind-template-in-depth.md b/src/data/posts/astrowind-template-in-depth.md
index c76ec50..3696689 100644
--- a/src/data/posts/astrowind-template-in-depth.md
+++ b/src/data/posts/astrowind-template-in-depth.md
@@ -3,6 +3,8 @@ pubDate: "Aug 08 2022"
title: "AstroWind template in depth"
description: "Ornare cum cursus laoreet sagittis nunc fusce posuere per euismod dis vehicula a, semper fames lacus maecenas dictumst pulvinar neque enim non potenti. Torquent hac sociosqu eleifend potenti."
image: "~/assets/images/hero.jpg"
+category: "Guide"
+tags: [astrowind]
---
## Dictum integer fusce ac ridiculus et odio sollicitudin diam at
diff --git a/src/data/posts/get-started-website-with-astro-tailwind-css.md b/src/data/posts/get-started-website-with-astro-tailwind-css.md
index 72ffdef..96ca2ce 100644
--- a/src/data/posts/get-started-website-with-astro-tailwind-css.md
+++ b/src/data/posts/get-started-website-with-astro-tailwind-css.md
@@ -4,6 +4,7 @@ title: "Get started with AstroWind to create a website using Astro and Tailwind
description: "Lorem ipsum dolor sit amet"
excerpt: "Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur nisi minim dolor. Aliquip et adipisicing sit sit fugiat"
image: "~/assets/images/steps.jpg"
+category: "Guide"
---
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
diff --git a/src/layouts/BlogLayout.astro b/src/layouts/BlogLayout.astro
new file mode 100644
index 0000000..34dc1f8
--- /dev/null
+++ b/src/layouts/BlogLayout.astro
@@ -0,0 +1,16 @@
+---
+import Layout from "~/layouts/PageLayout.astro";
+
+const { meta } = Astro.props;
+---
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/[...blog]/[...page].astro b/src/pages/[...blog]/[...page].astro
new file mode 100644
index 0000000..c422a83
--- /dev/null
+++ b/src/pages/[...blog]/[...page].astro
@@ -0,0 +1,39 @@
+---
+import { SITE, BLOG } from "~/config.mjs";
+import { fetchPosts } from "~/utils/fetchPosts";
+import Layout from "~/layouts/BlogLayout.astro";
+import BlogList from "~/components/widgets/BlogList.astro";
+import Pagination from "~/components/widgets/Pagination.astro";
+import { getCanonical, getPermalink } from "~/utils/permalinks";
+
+
+export async function getStaticPaths({ paginate }) {
+ if (BLOG?.disabled) return [];
+
+ const posts = await fetchPosts();
+
+ return paginate(posts, {
+ params: { blog: BLOG?.slug || undefined },
+ pageSize: BLOG.postsPerPage,
+ });
+}
+
+const { page } = Astro.props;
+const currentPage = page.currentPage ?? 1;
+
+const meta = {
+ title: `Blog ${currentPage > 1 ? `— Page ${currentPage} ` : ""}— ${SITE.name}`,
+ description: SITE.description,
+ canonical: getCanonical(getPermalink(page.url.current))
+}
+---
+
+
+
+ News and step-by-step guides about
+ AstroWind
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/[...blog]/[slug].astro b/src/pages/[...blog]/[slug].astro
new file mode 100644
index 0000000..13fe0ad
--- /dev/null
+++ b/src/pages/[...blog]/[slug].astro
@@ -0,0 +1,33 @@
+---
+import { SITE, BLOG } from "~/config.mjs";
+import { getCanonical, getPermalink } from "~/utils/permalinks";
+import { fetchPosts } from "~/utils/fetchPosts";
+import { findImage } from "~/utils/findImage";
+import Layout from "~/layouts/PageLayout.astro";
+import BlogPost from "~/components/widgets/BlogPost.astro";
+
+
+export async function getStaticPaths() {
+ if (BLOG?.disabled) return [];
+
+ const posts = await fetchPosts();
+
+ return posts.map((post) => ({
+ params: { slug: post.slug, blog: BLOG.postsWithoutBlogSlug ? undefined : BLOG?.slug || undefined },
+ props: { post },
+ }));
+}
+
+const { post } = Astro.props;
+
+const meta = {
+ title: `${post.title} — ${SITE.name}`,
+ description: post.description,
+ canonical: post.canonical || getCanonical(getPermalink(post.slug, "post")),
+ image: await findImage(post.image),
+}
+---
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/[...categories]/[category]/[...page].astro b/src/pages/[...categories]/[category]/[...page].astro
new file mode 100644
index 0000000..b35c745
--- /dev/null
+++ b/src/pages/[...categories]/[category]/[...page].astro
@@ -0,0 +1,46 @@
+---
+import { SITE, BLOG } from "~/config.mjs";
+import { fetchPosts } from "~/utils/fetchPosts";
+import Layout from "~/layouts/BlogLayout.astro";
+import BlogList from "~/components/widgets/BlogList.astro";
+import Pagination from "~/components/widgets/Pagination.astro";
+import { getCanonical, getPermalink } from "~/utils/permalinks";
+
+
+export async function getStaticPaths({ paginate }) {
+ if (BLOG?.disabled || BLOG?.category?.disabled) return [];
+
+ const posts = await fetchPosts();
+
+ const categories = new Set()
+ posts.map(post => {
+ typeof post.category === "string" && categories.add(post.category.toLowerCase())
+ })
+
+ return Array.from(categories).map((category) => (
+ paginate(posts.filter((post) => typeof post.category === "string" && category === post.category.toLowerCase()), {
+ params: { category, categories: BLOG?.category?.slug || undefined },
+ pageSize: BLOG.postsPerPage,
+ })
+ ))
+}
+
+const { page } = Astro.props;
+const { category } = Astro.params;
+
+const currentPage = page.currentPage ?? 1;
+
+const meta = {
+ title: `Category ${category} ${currentPage > 1 ? `— Page ${currentPage} ` : ""}— ${SITE.name}`,
+ description: SITE.description,
+ canonical: getCanonical(getPermalink(page.url.current))
+}
+---
+
+
+
+ Category {category}
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/[...tags]/[tag]/[...page].astro b/src/pages/[...tags]/[tag]/[...page].astro
new file mode 100644
index 0000000..fcd755c
--- /dev/null
+++ b/src/pages/[...tags]/[tag]/[...page].astro
@@ -0,0 +1,46 @@
+---
+import { SITE, BLOG } from "~/config.mjs";
+import { fetchPosts } from "~/utils/fetchPosts";
+import Layout from "~/layouts/BlogLayout.astro";
+import BlogList from "~/components/widgets/BlogList.astro";
+import Pagination from "~/components/widgets/Pagination.astro";
+import { getCanonical, getPermalink } from "~/utils/permalinks";
+
+
+export async function getStaticPaths({ paginate }) {
+ if (BLOG?.disabled || BLOG?.tag?.disabled) return [];
+
+ const posts = await fetchPosts();
+
+ const tags = new Set()
+ posts.map(post => {
+ Array.isArray(post.tags) && post.tags.map(tag => tags.add(tag.toLowerCase()))
+ })
+
+ return Array.from(tags).map((tag) => (
+ paginate(posts.filter((post) => Array.isArray(post.tags) && post.tags.includes(tag)), {
+ params: { tag, tags: BLOG?.tag?.slug || undefined },
+ pageSize: BLOG.postsPerPage,
+ })
+ ))
+}
+
+const { page } = Astro.props;
+const { tag } = Astro.params;
+
+const currentPage = page.currentPage ?? 1;
+
+const meta = {
+ title: `Posts by tag '${tag}' ${currentPage > 1 ? `— Page ${currentPage} ` : ""}— ${SITE.name}`,
+ description: SITE.description,
+ canonical: getCanonical(getPermalink(page.url.current))
+}
+---
+
+
+
+ Tag {tag}
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/blog/[...page].astro b/src/pages/blog/[...page].astro
deleted file mode 100644
index d1c4e47..0000000
--- a/src/pages/blog/[...page].astro
+++ /dev/null
@@ -1,28 +0,0 @@
----
-import Layout from "~/layouts/PageLayout.astro";
-
-import { SITE } from "~/config.mjs";
-import { fetchPosts } from "~/utils/fetchPosts";
-
-import BlogList from "~/components/widgets/BlogList.astro";
-
-export async function getStaticPaths({ paginate }) {
- const posts = await fetchPosts();
-
- return paginate(posts, {
- pageSize: SITE.postsPerPage,
- });
-}
-
-const { page } = Astro.props;
-
-const currentPage = page.currentPage ?? 1;
-
-const title = `Blog ${currentPage > 1 ? `— Page ${currentPage} ` : ""}— ${SITE.name}`;
-const description = SITE.description;
-const canonical = new URL(page.url.current, Astro.site);
----
-
-
-
-
\ No newline at end of file
diff --git a/src/pages/blog/[slug].astro b/src/pages/blog/[slug].astro
deleted file mode 100644
index 61fc6c9..0000000
--- a/src/pages/blog/[slug].astro
+++ /dev/null
@@ -1,29 +0,0 @@
----
-import { SITE } from "~/config.mjs";
-import { fetchPosts } from "~/utils/fetchPosts";
-import { findImage } from "~/utils/findImage";
-
-import Layout from "~/layouts/PageLayout.astro";
-import BlogPost from "~/components/widgets/BlogPost.astro";
-
-export async function getStaticPaths() {
- const posts = await fetchPosts();
-
- return posts.map((post) => ({
- params: { slug: post.slug },
- props: { post },
- }));
-}
-
-const { post } = Astro.props;
-
-const title = `${post.title} — ${SITE.name}`;
-const description = post.description;
-const canonical = post.canonical || new URL(`blog/${post.slug}`, Astro.site);
-
-const image = await findImage(post.image);
----
-
-
-
-
\ No newline at end of file
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 2cebbbb..1377576 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1,7 +1,7 @@
---
-import Layout from "~/layouts/PageLayout.astro";
-
import { SITE } from "~/config.mjs";
+import { getCanonical, getHomePermalink } from "~/utils/permalinks";
+import Layout from "~/layouts/PageLayout.astro";
import Hero from "~/components/widgets/Hero.astro";
import BasicCTA from "~/components/widgets/BasicCTA.astro";
@@ -13,12 +13,14 @@ import StepsLeft from "~/components/widgets/StepsLeft.astro";
import HighlightedPosts from "~/components/widgets/HighlightedPosts.astro";
import Stats from "~/components/widgets/Stats.astro";
-const title = SITE.title;
-const description = SITE.description;
-const canonical = new URL("", Astro.site);
+const meta = {
+ title: SITE.title,
+ description: SITE.description,
+ canonical: getCanonical(getHomePermalink()),
+}
---
-
+
diff --git a/src/pages/rss.xml.js b/src/pages/rss.xml.js
index ce67e47..b1a0fe8 100644
--- a/src/pages/rss.xml.js
+++ b/src/pages/rss.xml.js
@@ -2,6 +2,7 @@ import rss from "@astrojs/rss";
import { SITE } from "~/config.mjs";
import { fetchPosts } from "~/utils/fetchPosts";
+import { getPermalink } from "~/utils/permalinks";
const posts = await fetchPosts();
@@ -12,7 +13,7 @@ export const get = () =>
site: import.meta.env.SITE,
items: posts.map((post) => ({
- link: `blog/${post.slug}`,
+ link: getPermalink(post.slug, "post"),
title: post.title,
description: post.description,
pubDate: post.pubDate,
diff --git a/src/utils/fetchPosts.js b/src/utils/fetchPosts.js
index 17a142a..979612d 100644
--- a/src/utils/fetchPosts.js
+++ b/src/utils/fetchPosts.js
@@ -10,9 +10,9 @@ const load = async function () {
return await getNormalizedPost(post);
});
- const results = (await Promise.all(normalizedPosts)).sort(
- (a, b) => new Date(b.pubDate).valueOf() - new Date(a.pubDate).valueOf()
- );
+ const results = (await Promise.all(normalizedPosts))
+ .sort((a, b) => new Date(b.pubDate).valueOf() - new Date(a.pubDate).valueOf())
+ .filter((post) => !post.draft);
return results;
};
@@ -23,3 +23,16 @@ export const fetchPosts = async () => {
return await _posts;
};
+
+export const findPostsByIds = async (ids) => {
+ if (!Array.isArray(ids)) return [];
+
+ const posts = await fetchPosts();
+
+ return ids.reduce(function (r, a) {
+ posts.some(function (el) {
+ return a === el.slug && r.push(el);
+ });
+ return r;
+ }, []);
+};
diff --git a/src/utils/getNormalizedPost.js b/src/utils/getNormalizedPost.js
index 6e06b82..23f98cb 100644
--- a/src/utils/getNormalizedPost.js
+++ b/src/utils/getNormalizedPost.js
@@ -5,6 +5,7 @@ export const getNormalizedPost = async (post) => {
return {
pubDate: frontmatter.pubDate,
+ draft: frontmatter.draft,
canonical: frontmatter.canonical,
slug: file.split("/").pop().split(".").shift(),
diff --git a/src/utils/permalinks.js b/src/utils/permalinks.js
new file mode 100644
index 0000000..3b1e4f0
--- /dev/null
+++ b/src/utils/permalinks.js
@@ -0,0 +1,47 @@
+import { SITE, BLOG } from "~/config.mjs";
+
+const trim = (str, ch) => {
+ let start = 0, end = str.length;
+ while(start < end && str[start] === ch)
+ ++start;
+ while(end > start && str[end - 1] === ch)
+ --end;
+ return (start > 0 || end < str.length) ? str.substring(start, end) : str;
+}
+
+const trimSlash = (s) => trim(s, "/");
+const createPath = (...params) => "/" + params.filter((el) => !!el).join("/")
+
+const baseUrl = trimSlash(SITE.baseUrl);
+const blogBaseUrl = trimSlash(BLOG.slug);
+const categoryBaseUrl = trim(BLOG?.category?.slug);
+const tagBaseUrl = trim(BLOG?.tag?.slug);
+
+const cleanSlug = (slug) => trimSlash(slug);
+
+export const getCanonical = (path = "") => new URL(path, SITE.domain);
+
+export const getPermalink = (slug = "", type = "page") => {
+ const _slug = cleanSlug(slug);
+
+ switch (type) {
+ case "category":
+ return createPath(baseUrl, categoryBaseUrl, _slug)
+
+ case "tag":
+ return createPath(baseUrl, tagBaseUrl, _slug)
+
+ case "post":
+ return createPath(baseUrl, BLOG.postsWithoutBlogSlug ? "" : blogBaseUrl, _slug);
+
+ case "page":
+ default:
+ return createPath(baseUrl, _slug);
+ }
+};
+
+export const getBlogPermalink = () => getPermalink(blogBaseUrl);
+export const getHomePermalink = () => {
+ const permalink = getPermalink();
+ return permalink !== "/" ? permalink + "/" : permalink;
+}
\ No newline at end of file