Skip to content

Reorganize blog post structure for clarity - #50

Open
t3dotgg wants to merge 3 commits into
mainfrom
theo/new-blog-structure
Open

Reorganize blog post structure for clarity#50
t3dotgg wants to merge 3 commits into
mainfrom
theo/new-blog-structure

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Feb 2, 2026

Copy link
Copy Markdown
Collaborator

Summary:

  • Introduce new organization for blog content with active/archive separation and updated component/layout handling to support the structure
  • Refresh related assets, config, and utilities so previews and RSS still work with the new arrangement
    Testing:
  • Not run (not requested)

Note

Medium Risk
Moderate risk because it changes blog routing/content loading and introduces alias redirects/RSS/sitemap generation, which can break existing URLs or metadata if slugs/frontmatter are inconsistent.

Overview
Moves the blog from Astro.glob-based markdown loading to an astro:content collection (src/content.config.ts) with explicit canonical slug support, optional slugAliases redirects, and new draft/hidden/archived flags; posts are reorganized under src/content/posts/{active,archive}/.../index.md with updated frontmatter and relative/local images.

Adds new user-facing surfaces: an /rss.xml feed (via @astrojs/rss), a /blog/archive page listing archived posts, and sitemap generation (via @astrojs/sitemap) plus site/image defaults in astro.config.mjs. Blog rendering is updated to use render() for content entries, show optional cover images/archived banners, and remove the now-unused get-slug-from-path utility.

Improves SEO/social metadata in BaseLayout by adding canonical links, configurable OG/Twitter fields (including optional local image resizing), and article publish/updated times; analytics data-domain is updated to t3.gg.

Written by Cursor Bugbot for commit c4486e9. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • RSS feed for blog posts
    • Archive page for archived blog posts
    • Sitemap generation
    • Cover images for blog posts with responsive optimization
    • Enhanced SEO metadata including canonical URLs, Open Graph tags, and article publish/update timestamps
  • Documentation

    • Added blog post structure documentation
  • Style

    • Enhanced image styling and layout in blog content with centered presentation and border treatment

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Feb 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request migrates the blog system from file-based discovery to Astro Content Collections, introduces RSS feed generation and archive functionality, adds support for canonical slugs and slug aliases, enhances metadata handling with OG images and canonical URLs, and updates all blog posts with new frontmatter fields. Configuration changes enable sitemap generation and image optimization.

Changes

Cohort / File(s) Summary
Configuration & Dependencies
astro.config.mjs, package.json
Added site URL, image defaults with constrained layout, sitemap integration; added @astrojs/rss and @astrojs/sitemap dependencies.
Content Collection Setup
src/content.config.ts, src/content/posts/README.md
Introduced posts collection with glob loader, custom ID generation supporting canonical slugs, schema with frontmatter validation, and documentation for post organization and archival patterns.
Blog Post Content
src/content/posts/active/2020/*, src/content/posts/active/2021/*, src/content/posts/active/2022/*, src/content/posts/active/2024/*, src/content/posts/archive/2021/*, src/content/posts/archive/2023/*, src/content/posts/archive/2025/*
Updated all blog posts with new frontmatter fields (slug, cover, coverAlt, description, archived flags); replaced absolute imageURL references and HTML img tags with relative cover paths and Markdown image syntax.
Components
src/components/BlogImage.astro, src/components/BlogPostHeader.astro, src/components/BlogPostPreview.astro
Added BlogImage component for responsive local/remote image rendering; updated BlogPostHeader and BlogPostPreview to consume CollectionEntry posts with support for cover images and archived status badges; removed file-path-based slug extraction.
Pages & Routing
src/pages/blog/index.astro, src/pages/blog/post/[slug].astro, src/pages/blog/archive.astro, src/pages/rss.xml.ts
Migrated post discovery from glob to getCollection; added archive page filtering archived posts; implemented dynamic routes with slug aliases and canonical URL redirects; added RSS feed endpoint with post excerpts and metadata.
Layout & Styling
src/layouts/BaseLayout.astro, src/style/global.css
Extended BaseLayout with OG image handling, canonical URL support, and article metadata (published/updated times); added CSS styling for centered blog prose images with borders.
Utilities
src/utils/get-slug-from-path.ts
Removed getSlugFromPath utility; slug management now handled via content collection configuration and explicit frontmatter fields.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant ContentLoader as Content<br/>Loader
    participant Filter as Filter &<br/>Sort
    participant MetadataGen as Metadata<br/>Generator
    participant Renderer as Renderer
    participant Assets as Asset<br/>Handler

    Browser->>ContentLoader: Request blog post [slug]
    ContentLoader->>ContentLoader: Load post from collection
    Filter->>Filter: Check visibility flags<br/>(hidden, draft, archived)
    Filter->>MetadataGen: Pass filtered post
    MetadataGen->>MetadataGen: Extract canonical slug<br/>from post.id
    MetadataGen->>MetadataGen: Resolve OG image<br/>(cover or imageURL)
    MetadataGen->>Assets: getImage for OG<br/>(1200x630)
    Assets-->>MetadataGen: Image metadata
    MetadataGen->>Renderer: Pass post + metadata<br/>(title, description,<br/>canonical URL)
    alt isAlias route
        Renderer->>Browser: Render meta refresh<br/>to canonical URL
    else canonical route
        Renderer->>Renderer: Render BlogPostHeader<br/>with cover image
        Renderer->>Renderer: Render post Content
        Renderer->>Browser: Full post page
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 From files in folders to collections we go,
With slugs in the frontmatter, now neat and in tow,
Archive and RSS dance hand in hand,
OG images gleaming across the land,
A rabbit-approved refactor, perfectly planned! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Reorganize blog post structure for clarity' directly aligns with the main change: reorganizing blog posts into active/archive folders and updating related components and configuration to support this new structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch theo/new-blog-structure

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel

vercel Bot commented Feb 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
t3-astro Ready Ready Preview, Comment Feb 2, 2026 3:34am

Request Review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@astro.config.mjs`:
- Around line 2-3: Remove the deprecated integration import and any references
to it: delete the line importing prefetch ("import prefetch from
\"@astrojs/prefetch\";") and remove "prefetch" from the Astro integrations list
or any place it's used in astro.config.mjs; rely on Astro 5's built-in
prefetching and remove any configuration or code that expects the
`@astrojs/prefetch` integration (search for the symbol "prefetch" to find usages).
- Around line 7-18: Remove the prefetch() integration from the integrations
array and instead enable Astro's built-in prefetching; specifically, edit the
defineConfig block to drop prefetch() from integrations (leave integrations:
[sitemap()]) and optionally add an experimental prefetch flag inside the config
(e.g., add an experimental: { prefetch: true } property) so built-in prefetching
is used rather than the external prefetch() integration.

In `@package.json`:
- Around line 24-27: Remove the deprecated "@astrojs/prefetch" dependency from
package.json and remove any related import/registration in astro.config.mjs
(look for occurrences of the string "@astrojs/prefetch" and the integration
variable/entry used to register it, e.g., an import or an integrations array
entry); then update astro.config.mjs to rely on Astro 5's built-in prefetch
(i.e., delete the prefetch integration entry and any config options you added
solely for that integration) and update your templates to use
data-astro-prefetch on links (e.g., check your .astro/.html files for <a>
elements to optionally add data-astro-prefetch or data-astro-prefetch="tap").
Ensure package.json no longer lists "@astrojs/prefetch" and run npm/yarn install
to update lockfile.

In `@src/components/BlogPostHeader.astro`:
- Around line 12-22: The time element currently sets datetime using
date.toString(), which can produce non-ISO values; update the datetime attribute
in BlogPostHeader's <time> to an ISO-formatted string (e.g., use
date.toISOString() or formatISO(date) if you're using date-fns) instead of
date.toString(), ensuring you reference the existing date variable used to build
formatted so the attribute contains a valid ISO 8601 datetime.

In `@src/pages/blog/post/`[slug].astro:
- Around line 43-54: The nullish coalescing chain is wrong because
fallbackDescription is always a string (possibly empty), so fallbackDescription
?? "Thought dumps and nerdy stuff" never yields the default; update the
assignment for description (symbol: description) to treat empty strings as
falsy—either use logical OR: post.data.description || fallbackDescription ||
"Thought dumps and nerdy stuff", or perform an explicit length check on
post.data.description and fallbackDescription (symbols: post.data.description,
fallbackDescription) and choose the first non-empty string.
🧹 Nitpick comments (3)
src/components/BlogImage.astro (1)

40-46: Consider adding sizes and srcset for remote images.

Remote images currently don't benefit from responsive sizing. If remote images are commonly used, consider adding width/height attributes or using Astro's remote image optimization.

     <img
       src={src}
       alt={alt}
       loading={loading}
       decoding="async"
       class={className}
+      sizes={sizes}
     />
src/pages/blog/post/[slug].astro (1)

78-84: Consider moving meta refresh to head or using HTTP redirects.

The <meta http-equiv="refresh"> tag inside the body is non-standard HTML (should be in <head>). While browsers typically process it anyway, consider:

  1. Moving the meta tag to the head via BaseLayout (e.g., pass a redirectUrl prop)
  2. Using Astro's redirect configuration for proper HTTP 301 redirects, which is better for SEO

This is a functional approach for now, but HTTP redirects would preserve link equity better.

src/layouts/BaseLayout.astro (1)

66-80: Consider error handling for getImage call.

The getImage call at lines 72-78 could throw if the image source is invalid or the image processing fails. While this would surface at build time, consider whether graceful fallback to the default OG image is desirable.

🛡️ Optional: Add try-catch for resilience
 if (ogImage) {
   if (typeof ogImage === "string") {
     resolvedOgImageHref = ogImage.startsWith("http")
       ? ogImage
       : new URL(ogImage, siteUrl).toString();
   } else {
-    const generatedOgImage = await getImage({
-      src: ogImage,
-      width: 1200,
-      height: 630,
-      format: "png",
-    });
-    resolvedOgImageHref = new URL(generatedOgImage.src, siteUrl).toString();
+    try {
+      const generatedOgImage = await getImage({
+        src: ogImage,
+        width: 1200,
+        height: 630,
+        format: "png",
+      });
+      resolvedOgImageHref = new URL(generatedOgImage.src, siteUrl).toString();
+    } catch {
+      // Fall back to default OG image on processing failure
+    }
   }
 }

Comment thread astro.config.mjs
Comment on lines 2 to +3
import prefetch from "@astrojs/prefetch";
import sitemap from "@astrojs/sitemap";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove deprecated @astrojs/prefetch integration.

As noted in package.json, @astrojs/prefetch is incompatible with Astro 5. Remove the import and use built-in prefetching.

 import { defineConfig } from "astro/config";
-import prefetch from "@astrojs/prefetch";
 import sitemap from "@astrojs/sitemap";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import prefetch from "@astrojs/prefetch";
import sitemap from "@astrojs/sitemap";
import { defineConfig } from "astro/config";
import sitemap from "@astrojs/sitemap";
🤖 Prompt for AI Agents
In `@astro.config.mjs` around lines 2 - 3, Remove the deprecated integration
import and any references to it: delete the line importing prefetch ("import
prefetch from \"@astrojs/prefetch\";") and remove "prefetch" from the Astro
integrations list or any place it's used in astro.config.mjs; rely on Astro 5's
built-in prefetching and remove any configuration or code that expects the
`@astrojs/prefetch` integration (search for the symbol "prefetch" to find usages).

Comment thread astro.config.mjs
Comment on lines 7 to +18
export default defineConfig({
site: "https://t3.gg",
image: {
// Apply responsive image defaults to Markdown content.
layout: "constrained",
domains: ["t3.gg", "img.youtube.com"],
},
vite: {
plugins: [tailwindcss()],
},
plugins: [],
integrations: [prefetch()],
integrations: [prefetch(), sitemap()],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Site and image configuration look good; update integrations.

The site URL is required for sitemap/RSS generation, and the image configuration properly enables responsive images with appropriate domains.

Update the integrations array to remove prefetch and optionally enable built-in prefetching:

 export default defineConfig({
   site: "https://t3.gg",
+  prefetch: true,
   image: {
     layout: "constrained",
     domains: ["t3.gg", "img.youtube.com"],
   },
   // ...
-  integrations: [prefetch(), sitemap()],
+  integrations: [sitemap()],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default defineConfig({
site: "https://t3.gg",
image: {
// Apply responsive image defaults to Markdown content.
layout: "constrained",
domains: ["t3.gg", "img.youtube.com"],
},
vite: {
plugins: [tailwindcss()],
},
plugins: [],
integrations: [prefetch()],
integrations: [prefetch(), sitemap()],
export default defineConfig({
site: "https://t3.gg",
prefetch: true,
image: {
// Apply responsive image defaults to Markdown content.
layout: "constrained",
domains: ["t3.gg", "img.youtube.com"],
},
vite: {
plugins: [tailwindcss()],
},
plugins: [],
integrations: [sitemap()],
});
🤖 Prompt for AI Agents
In `@astro.config.mjs` around lines 7 - 18, Remove the prefetch() integration from
the integrations array and instead enable Astro's built-in prefetching;
specifically, edit the defineConfig block to drop prefetch() from integrations
(leave integrations: [sitemap()]) and optionally add an experimental prefetch
flag inside the config (e.g., add an experimental: { prefetch: true } property)
so built-in prefetching is used rather than the external prefetch() integration.

Comment thread package.json
Comment on lines 24 to 27
"@astrojs/prefetch": "^0.4.1",
"@astrojs/rss": "^4.0.15",
"@astrojs/sitemap": "^3.7.0",
"date-fns": "^4.1.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat package.json | head -50

Repository: T3-Content/t3.gg

Length of output: 833


🏁 Script executed:

test -f astro.config.mjs && cat astro.config.mjs || echo "astro.config.mjs not found"

Repository: T3-Content/t3.gg

Length of output: 624


🌐 Web query:

Astro 5 built-in prefetch configuration

💡 Result:

Astro 5 has built-in link prefetching (the old @astrojs/prefetch integration is deprecated/replaced). [5]

Enable it (project-wide)

In astro.config.mjs:

import { defineConfig } from 'astro/config';

export default defineConfig({
  prefetch: true,
});

This injects the prefetch runtime so you can opt-in per link with data-astro-prefetch. [1][2]

Prefetch configuration options

You can also pass an object:

export default defineConfig({
  prefetch: {
    defaultStrategy: 'viewport', // 'hover' | 'tap' | 'viewport' | 'load'
    prefetchAll: false,          // true = prefetch all internal links by default
  },
});
  • defaultStrategy: default is 'hover'; available strategies are hover (default), tap, viewport, load. [1][2]
  • prefetchAll: when true, all internal links are prefetched by default; you can opt out per-link with data-astro-prefetch="false". [1][2]

Per-link usage

Opt in:

<a href="/about" data-astro-prefetch>About</a>

Pick a strategy per link:

<a href="/about" data-astro-prefetch="tap">About</a>

Opt out (when prefetchAll: true):

<a href="/about" data-astro-prefetch="false">About</a>

(Works for internal links only.) [1]

Optional: client prerendering (experimental)

If you want prefetched pages to be prerendered in supported browsers (Speculation Rules API):

export default defineConfig({
  prefetch: { prefetchAll: true, defaultStrategy: 'viewport' },
  experimental: { clientPrerender: true },
});

[3]


🌐 Web query:

@astrojs/prefetch deprecation Astro 5 removal

💡 Result:

  • @astrojs/prefetch was deprecated in Astro v3.5.0 and replaced by Astro’s built-in prefetch feature. The docs explicitly say the integration “will eventually be removed entirely,” but they do not commit to a specific Astro 5.x removal version/date. [1] [2]
  • The npm package itself is also marked deprecated, instructing you to use the built-in prefetch option and follow the migration guide. [3]

What to do for Astro 5: remove @astrojs/prefetch, enable prefetch in astro.config.*, and use data-astro-prefetch / data-astro-prefetch="viewport" on links as needed. [1]

Sources: [1] [2] [3]


Replace deprecated @astrojs/prefetch with Astro 5's built-in prefetch configuration.

The @astrojs/prefetch integration was deprecated in Astro 3.5 and is marked deprecated on npm. Astro 5 includes built-in prefetching, making this dependency unnecessary.

Remove from package.json:

package.json diff
  "dependencies": {
-   "@astrojs/prefetch": "^0.4.1",
    "@astrojs/rss": "^4.0.15",
    "@astrojs/sitemap": "^3.7.0",
    "date-fns": "^4.1.0"
  }

Update astro.config.mjs:

astro.config.mjs diff
  import { defineConfig } from "astro/config";
- import prefetch from "@astrojs/prefetch";
  import sitemap from "@astrojs/sitemap";
  import tailwindcss from "@tailwindcss/vite";

  export default defineConfig({
    site: "https://t3.gg",
    image: {
      layout: "constrained",
      domains: ["t3.gg", "img.youtube.com"],
    },
    vite: {
      plugins: [tailwindcss()],
    },
    plugins: [],
-   integrations: [prefetch(), sitemap()],
+   integrations: [sitemap()],
+   prefetch: true,
    redirects: {
      "/links": "/",
      "/faq": "/",
    },
  });

Use data-astro-prefetch on individual links to control prefetching behavior:

<a href="/about" data-astro-prefetch>About</a>
<a href="/contact" data-astro-prefetch="tap">Contact</a>
🤖 Prompt for AI Agents
In `@package.json` around lines 24 - 27, Remove the deprecated "@astrojs/prefetch"
dependency from package.json and remove any related import/registration in
astro.config.mjs (look for occurrences of the string "@astrojs/prefetch" and the
integration variable/entry used to register it, e.g., an import or an
integrations array entry); then update astro.config.mjs to rely on Astro 5's
built-in prefetch (i.e., delete the prefetch integration entry and any config
options you added solely for that integration) and update your templates to use
data-astro-prefetch on links (e.g., check your .astro/.html files for <a>
elements to optionally add data-astro-prefetch or data-astro-prefetch="tap").
Ensure package.json no longer lists "@astrojs/prefetch" and run npm/yarn install
to update lockfile.

Comment on lines +12 to 22
const date = post.data.date;
const formatted = format(date, "LLLL d, yyyy");
const coverImage = post.data.cover ?? post.data.imageURL;
const coverAlt = post.data.coverAlt ?? post.data.title;
---

<header class="not-prose mb-12 border-b border-white/10 pb-8">
<h1 class="mb-3 text-2xl font-medium tracking-tight text-text">
{post.title}
{post.data.title}
</h1>
<time datetime={date.toString()} class="text-sm text-muted">{formatted}</time>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use ISO format for the <time> datetime attribute.
Line 22 uses date.toString(), which isn't guaranteed to be a valid datetime attribute value. Prefer ISO.

🛠️ Suggested fix
-  <time datetime={date.toString()} class="text-sm text-muted">{formatted}</time>
+  <time datetime={date.toISOString()} class="text-sm text-muted">{formatted}</time>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const date = post.data.date;
const formatted = format(date, "LLLL d, yyyy");
const coverImage = post.data.cover ?? post.data.imageURL;
const coverAlt = post.data.coverAlt ?? post.data.title;
---
<header class="not-prose mb-12 border-b border-white/10 pb-8">
<h1 class="mb-3 text-2xl font-medium tracking-tight text-text">
{post.title}
{post.data.title}
</h1>
<time datetime={date.toString()} class="text-sm text-muted">{formatted}</time>
const date = post.data.date;
const formatted = format(date, "LLLL d, yyyy");
const coverImage = post.data.cover ?? post.data.imageURL;
const coverAlt = post.data.coverAlt ?? post.data.title;
---
<header class="not-prose mb-12 border-b border-white/10 pb-8">
<h1 class="mb-3 text-2xl font-medium tracking-tight text-text">
{post.data.title}
</h1>
<time datetime={date.toISOString()} class="text-sm text-muted">{formatted}</time>
🤖 Prompt for AI Agents
In `@src/components/BlogPostHeader.astro` around lines 12 - 22, The time element
currently sets datetime using date.toString(), which can produce non-ISO values;
update the datetime attribute in BlogPostHeader's <time> to an ISO-formatted
string (e.g., use date.toISOString() or formatISO(date) if you're using
date-fns) instead of date.toString(), ensuring you reference the existing date
variable used to build formatted so the attribute contains a valid ISO 8601
datetime.

Comment on lines +43 to +54
const fallbackDescription = post.body
.replace(/```[\s\S]*?```/g, " ")
.replace(/<[^>]+>/g, " ")
.replace(/[#*_>`~\-!\[\]\(\)]/g, " ")
.replace(/\s+/g, " ")
.trim()
.slice(0, 160);

const description =
post.data.description ??
fallbackDescription ??
"Thought dumps and nerdy stuff";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Redundant fallback in nullish coalescing chain.

fallbackDescription will always be a string (possibly empty "") after .slice(), so fallbackDescription ?? "Thought dumps..." will never fall through to the default string because "" is not nullish.

If an empty description should use the fallback, use logical OR or check length explicitly.

🛠️ Proposed fix
 const description =
   post.data.description ??
-  fallbackDescription ??
-  "Thought dumps and nerdy stuff";
+  (fallbackDescription || "Thought dumps and nerdy stuff");

Or alternatively, for explicit intent:

 const description =
   post.data.description ??
-  fallbackDescription ??
-  "Thought dumps and nerdy stuff";
+  (fallbackDescription.length > 0 ? fallbackDescription : "Thought dumps and nerdy stuff");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fallbackDescription = post.body
.replace(/```[\s\S]*?```/g, " ")
.replace(/<[^>]+>/g, " ")
.replace(/[#*_>`~\-!\[\]\(\)]/g, " ")
.replace(/\s+/g, " ")
.trim()
.slice(0, 160);
const description =
post.data.description ??
fallbackDescription ??
"Thought dumps and nerdy stuff";
const fallbackDescription = post.body
.replace(/
🤖 Prompt for AI Agents
In `@src/pages/blog/post/`[slug].astro around lines 43 - 54, The nullish
coalescing chain is wrong because fallbackDescription is always a string
(possibly empty), so fallbackDescription ?? "Thought dumps and nerdy stuff"
never yields the default; update the assignment for description (symbol:
description) to treat empty strings as falsy—either use logical OR:
post.data.description || fallbackDescription || "Thought dumps and nerdy stuff",
or perform an explicit length check on post.data.description and
fallbackDescription (symbols: post.data.description, fallbackDescription) and
choose the first non-empty string.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

<BaseLayout
{title}
{description}
canonicalUrl={post.data.canonicalUrl}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alias pages have incorrect canonical URL for SEO

Medium Severity

For alias pages, canonicalUrl={post.data.canonicalUrl} is passed to BaseLayout. Since post.data.canonicalUrl is typically undefined for internal posts (it's meant for external canonical URLs), BaseLayout falls back to using the current page's URL path. This means an alias page at /blog/post/old-slug gets a canonical link pointing to itself instead of the actual canonical URL /blog/post/${canonicalSlug}. This defeats the SEO purpose of slug aliases.

Fix in Cursor Fix in Web

const description =
post.data.description ??
fallbackDescription ??
"Thought dumps and nerdy stuff";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default description fallback is unreachable due to nullish coalescing

Low Severity

The final fallback "Thought dumps and nerdy stuff" is unreachable. Since fallbackDescription is always a string (even empty string ""), the nullish coalescing operator ?? never triggers the last fallback. If a post has no explicit description and empty/minimal body content that gets stripped to "", the meta description will be an empty string instead of the intended default text.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant