the-grid-cc 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.grid/plans/blog-PLAN-SUMMARY.md +518 -0
- package/.grid/plans/blog-block-01.md +180 -0
- package/.grid/plans/blog-block-02.md +229 -0
- package/.grid/plans/blog-block-03.md +253 -0
- package/.grid/plans/blog-block-04.md +287 -0
- package/.grid/plans/blog-block-05.md +235 -0
- package/.grid/plans/blog-block-06.md +325 -0
- package/commands/grid/VERSION +1 -1
- package/commands/grid/mc.md +48 -13
- package/package.json +1 -1
- package/test-cli/converter.py +206 -0
- package/test-cli/test_data.json +39 -0
- package/test-cli/test_data.yaml +35 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
---
|
|
2
|
+
cluster: james-weatherhead-blog
|
|
3
|
+
block: 04
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 3
|
|
6
|
+
depends_on: [02, 03]
|
|
7
|
+
files_modified: [src/pages/index.astro, src/pages/blog/index.astro, src/pages/about.astro, src/pages/blog/tags/[tag].astro]
|
|
8
|
+
autonomous: false
|
|
9
|
+
|
|
10
|
+
must_haves:
|
|
11
|
+
truths:
|
|
12
|
+
- "Homepage displays recent blog posts"
|
|
13
|
+
- "Blog index page lists all posts"
|
|
14
|
+
- "About page contains author information"
|
|
15
|
+
- "Tag pages filter posts by tag"
|
|
16
|
+
artifacts:
|
|
17
|
+
- path: "src/pages/index.astro"
|
|
18
|
+
provides: "Homepage with recent posts"
|
|
19
|
+
min_lines: 30
|
|
20
|
+
- path: "src/pages/blog/index.astro"
|
|
21
|
+
provides: "Complete blog post listing"
|
|
22
|
+
min_lines: 25
|
|
23
|
+
- path: "src/pages/about.astro"
|
|
24
|
+
provides: "About page with author info"
|
|
25
|
+
min_lines: 20
|
|
26
|
+
- path: "src/pages/blog/tags/[tag].astro"
|
|
27
|
+
provides: "Tag-filtered post listing"
|
|
28
|
+
min_lines: 40
|
|
29
|
+
key_links:
|
|
30
|
+
- from: "index.astro"
|
|
31
|
+
to: "getCollection('blog')"
|
|
32
|
+
via: "fetch recent posts"
|
|
33
|
+
pattern: "getCollection\\('blog'\\)"
|
|
34
|
+
- from: "[tag].astro"
|
|
35
|
+
to: "posts.filter"
|
|
36
|
+
via: "tag filtering"
|
|
37
|
+
pattern: "\\.filter.*tags\\.includes"
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<objective>
|
|
41
|
+
Create static pages (homepage, blog index, about) and tag-based filtering functionality.
|
|
42
|
+
|
|
43
|
+
Purpose: Connect layout system (Block 02) and content system (Block 03) into navigable pages with tag-based organization.
|
|
44
|
+
Output: Complete page structure with homepage, blog listing, about page, and tag filtering.
|
|
45
|
+
</objective>
|
|
46
|
+
|
|
47
|
+
<context>
|
|
48
|
+
Dependencies completed:
|
|
49
|
+
- Block 02: Layout system (BaseLayout, Header, Footer, DarkModeToggle)
|
|
50
|
+
- Block 03: Content system (blog collection, post template, PostList component)
|
|
51
|
+
|
|
52
|
+
This block brings everything together into actual pages users will visit. Navigation links from Header (Home, Blog, About) now have destinations. Tag filtering enables content organization without complex search.
|
|
53
|
+
|
|
54
|
+
This is where the blog becomes navigable and complete. After this block, users can:
|
|
55
|
+
- Land on homepage and see recent posts
|
|
56
|
+
- Browse all posts at /blog
|
|
57
|
+
- Read about the author at /about
|
|
58
|
+
- Filter posts by tag at /blog/tags/{tag}
|
|
59
|
+
</context>
|
|
60
|
+
|
|
61
|
+
<threads>
|
|
62
|
+
|
|
63
|
+
<thread type="auto">
|
|
64
|
+
<name>Thread 1: Build homepage and blog index</name>
|
|
65
|
+
<files>src/pages/index.astro, src/pages/blog/index.astro</files>
|
|
66
|
+
<action>
|
|
67
|
+
Create homepage and blog listing pages:
|
|
68
|
+
|
|
69
|
+
index.astro (Homepage):
|
|
70
|
+
1. Import BaseLayout, PostList, getCollection
|
|
71
|
+
2. Fetch recent posts:
|
|
72
|
+
```ts
|
|
73
|
+
const allPosts = await getCollection('blog', ({ data }) => !data.draft);
|
|
74
|
+
const recentPosts = allPosts
|
|
75
|
+
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
|
|
76
|
+
.slice(0, 5); // Show 5 most recent
|
|
77
|
+
```
|
|
78
|
+
3. Hero section:
|
|
79
|
+
- Site title: "James Weatherhead"
|
|
80
|
+
- Subtitle/tagline (e.g., "Software Engineer & Technical Writer")
|
|
81
|
+
- Brief intro paragraph (1-2 sentences)
|
|
82
|
+
4. Recent posts section:
|
|
83
|
+
- Heading: "Recent Posts"
|
|
84
|
+
- Use PostList component with recentPosts
|
|
85
|
+
- Link to /blog: "View all posts →"
|
|
86
|
+
5. Minimal design: max-w-3xl container, generous whitespace
|
|
87
|
+
|
|
88
|
+
blog/index.astro (Blog Index):
|
|
89
|
+
1. Import BaseLayout, PostList, getCollection
|
|
90
|
+
2. Fetch all posts (sorted by date descending)
|
|
91
|
+
3. Page title: "Blog"
|
|
92
|
+
4. Optional: display tag cloud (all unique tags)
|
|
93
|
+
5. Use PostList component with all posts
|
|
94
|
+
6. Style consistently with homepage
|
|
95
|
+
|
|
96
|
+
AVOID: Do not add complex hero animations (keep minimal). Do not add featured posts section yet (YAGNI). WHY: Requirements specify "minimal design" - focus on content, not embellishments.
|
|
97
|
+
</action>
|
|
98
|
+
<verify>
|
|
99
|
+
Run: npm run dev
|
|
100
|
+
Visit: http://localhost:4321 (homepage)
|
|
101
|
+
Check: Recent posts appear (up to 5)
|
|
102
|
+
Visit: http://localhost:4321/blog (blog index)
|
|
103
|
+
Check: All posts appear in list
|
|
104
|
+
Check: Both pages use BaseLayout with header/footer
|
|
105
|
+
</verify>
|
|
106
|
+
<done>
|
|
107
|
+
- index.astro displays hero section and 5 recent posts
|
|
108
|
+
- blog/index.astro lists all non-draft posts
|
|
109
|
+
- Both pages use consistent styling (max-w-3xl, Tailwind)
|
|
110
|
+
- "View all posts" link navigates to /blog
|
|
111
|
+
- Posts sorted by date descending
|
|
112
|
+
</done>
|
|
113
|
+
</thread>
|
|
114
|
+
|
|
115
|
+
<thread type="auto">
|
|
116
|
+
<name>Thread 2: Create About page</name>
|
|
117
|
+
<files>src/pages/about.astro</files>
|
|
118
|
+
<action>
|
|
119
|
+
Create About page with author information:
|
|
120
|
+
|
|
121
|
+
1. Create src/pages/about.astro with BaseLayout
|
|
122
|
+
2. Page title: "About"
|
|
123
|
+
3. Content structure:
|
|
124
|
+
- Heading: "About James Weatherhead"
|
|
125
|
+
- Bio paragraphs (placeholder text for now - user will customize):
|
|
126
|
+
* Professional background
|
|
127
|
+
* Technical interests
|
|
128
|
+
* Why they write (blog purpose)
|
|
129
|
+
- Contact information (if provided)
|
|
130
|
+
- Optional: Links to GitHub, LinkedIn, Twitter (if provided)
|
|
131
|
+
4. Style with prose typography: prose dark:prose-invert max-w-3xl
|
|
132
|
+
5. Consistent container width with other pages
|
|
133
|
+
|
|
134
|
+
Placeholder content example:
|
|
135
|
+
"I'm a software engineer passionate about web development, DevOps, and technical writing. This blog is where I share insights, tutorials, and lessons learned from building software."
|
|
136
|
+
|
|
137
|
+
AVOID: Do not add contact form (YAGNI - add if requested). Do not add photo upload (user can add later). WHY: Keep initial version simple - user can customize content after deployment.
|
|
138
|
+
</action>
|
|
139
|
+
<verify>
|
|
140
|
+
Visit: http://localhost:4321/about
|
|
141
|
+
Check: Page renders with heading and bio content
|
|
142
|
+
Check: Consistent styling with other pages (max-w-3xl, prose)
|
|
143
|
+
Check: Dark mode works correctly on about page
|
|
144
|
+
Check: Header navigation links to /about works
|
|
145
|
+
</verify>
|
|
146
|
+
<done>
|
|
147
|
+
- about.astro created with BaseLayout
|
|
148
|
+
- Page contains heading and placeholder bio content
|
|
149
|
+
- Prose typography applied for readability
|
|
150
|
+
- Consistent container width (max-w-3xl)
|
|
151
|
+
- Navigation link from Header works
|
|
152
|
+
</done>
|
|
153
|
+
</thread>
|
|
154
|
+
|
|
155
|
+
<thread type="auto">
|
|
156
|
+
<name>Thread 3: Implement tag-based filtering</name>
|
|
157
|
+
<files>src/pages/blog/tags/[tag].astro</files>
|
|
158
|
+
<action>
|
|
159
|
+
Create dynamic tag pages for filtering posts by tag:
|
|
160
|
+
|
|
161
|
+
1. Create src/pages/blog/tags/[tag].astro
|
|
162
|
+
|
|
163
|
+
2. Implement getStaticPaths to generate page for each unique tag:
|
|
164
|
+
```ts
|
|
165
|
+
import { getCollection } from 'astro:content';
|
|
166
|
+
export async function getStaticPaths() {
|
|
167
|
+
const allPosts = await getCollection('blog', ({ data }) => !data.draft);
|
|
168
|
+
const uniqueTags = [...new Set(allPosts.flatMap(post => post.data.tags))];
|
|
169
|
+
|
|
170
|
+
return uniqueTags.map(tag => {
|
|
171
|
+
const filteredPosts = allPosts.filter(post =>
|
|
172
|
+
post.data.tags.includes(tag)
|
|
173
|
+
);
|
|
174
|
+
return {
|
|
175
|
+
params: { tag },
|
|
176
|
+
props: { posts: filteredPosts },
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
3. Render tag page:
|
|
183
|
+
- Page title: "Posts tagged with '{tag}'"
|
|
184
|
+
- Display tag name prominently
|
|
185
|
+
- Show post count: "X posts"
|
|
186
|
+
- Use PostList component with filtered posts
|
|
187
|
+
- Breadcrumb: "Blog / Tags / {tag}"
|
|
188
|
+
- Link back to /blog
|
|
189
|
+
|
|
190
|
+
4. Update Tag component (from Block 03):
|
|
191
|
+
- When variant='link', link to /blog/tags/{tag}
|
|
192
|
+
- Ensure tags in PostList are clickable
|
|
193
|
+
|
|
194
|
+
AVOID: Do not add tag description field (YAGNI - tags are self-explanatory). Do not add "related tags" section (over-engineering). WHY: Keep tag pages simple - they're for filtering, not complex taxonomy.
|
|
195
|
+
</action>
|
|
196
|
+
<verify>
|
|
197
|
+
Run: npm run dev
|
|
198
|
+
Visit: http://localhost:4321/blog/tags/javascript (or any tag from sample post)
|
|
199
|
+
Check: Page shows only posts with that tag
|
|
200
|
+
Check: Post count displays correctly
|
|
201
|
+
Check: Tags in PostList are clickable and link to tag pages
|
|
202
|
+
Check: Breadcrumb or back link to /blog exists
|
|
203
|
+
Run: npm run build (should generate static page for each tag)
|
|
204
|
+
</verify>
|
|
205
|
+
<done>
|
|
206
|
+
- [tag].astro implements getStaticPaths with unique tags
|
|
207
|
+
- Filters posts by tag correctly
|
|
208
|
+
- Displays tag name and post count
|
|
209
|
+
- PostList shows filtered posts
|
|
210
|
+
- Tag component links to tag pages (variant='link')
|
|
211
|
+
- Build generates static page for each tag
|
|
212
|
+
</done>
|
|
213
|
+
</thread>
|
|
214
|
+
|
|
215
|
+
<thread type="checkpoint:human-verify" gate="blocking">
|
|
216
|
+
<what-built>
|
|
217
|
+
Complete page structure:
|
|
218
|
+
- Homepage (index.astro) with recent posts
|
|
219
|
+
- Blog index (blog/index.astro) with all posts
|
|
220
|
+
- About page (about.astro) with author info
|
|
221
|
+
- Tag pages (blog/tags/[tag].astro) with filtered posts
|
|
222
|
+
</what-built>
|
|
223
|
+
<how-to-verify>
|
|
224
|
+
1. cd /Users/jacweath/grid/blog && npm run dev
|
|
225
|
+
|
|
226
|
+
2. Test Homepage (http://localhost:4321):
|
|
227
|
+
- Hero section displays site title and intro
|
|
228
|
+
- Recent posts section shows up to 5 posts
|
|
229
|
+
- "View all posts" link navigates to /blog
|
|
230
|
+
- Header navigation visible (Home, Blog, About)
|
|
231
|
+
|
|
232
|
+
3. Test Blog Index (http://localhost:4321/blog):
|
|
233
|
+
- All blog posts listed
|
|
234
|
+
- Posts sorted newest first
|
|
235
|
+
- Tags visible and clickable
|
|
236
|
+
|
|
237
|
+
4. Test About Page (http://localhost:4321/about):
|
|
238
|
+
- Page renders with author info
|
|
239
|
+
- Consistent styling with other pages
|
|
240
|
+
- Navigation link from Header works
|
|
241
|
+
|
|
242
|
+
5. Test Tag Filtering:
|
|
243
|
+
- Click a tag from blog index or post page
|
|
244
|
+
- Should navigate to /blog/tags/{tag}
|
|
245
|
+
- Only posts with that tag appear
|
|
246
|
+
- Post count displays correctly
|
|
247
|
+
|
|
248
|
+
6. Test Navigation:
|
|
249
|
+
- Click each nav link (Home, Blog, About)
|
|
250
|
+
- All pages load without errors
|
|
251
|
+
- Dark mode toggle works on all pages
|
|
252
|
+
|
|
253
|
+
7. Run: npm run build
|
|
254
|
+
- Build completes successfully
|
|
255
|
+
- Check dist/ contains all static pages
|
|
256
|
+
</how-to-verify>
|
|
257
|
+
<resume-signal>Type "approved" if all pages work and navigation is functional, or describe issues (e.g., "tag filtering broken", "about page missing", "homepage posts not showing")</resume-signal>
|
|
258
|
+
</thread>
|
|
259
|
+
|
|
260
|
+
</threads>
|
|
261
|
+
|
|
262
|
+
<verification>
|
|
263
|
+
Complete verification checklist:
|
|
264
|
+
1. Homepage renders with hero and recent posts (max 5)
|
|
265
|
+
2. Blog index lists all non-draft posts sorted by date
|
|
266
|
+
3. About page displays author information
|
|
267
|
+
4. Tag pages filter posts correctly by tag
|
|
268
|
+
5. Navigation links work (Home, Blog, About)
|
|
269
|
+
6. Tags are clickable and link to tag pages
|
|
270
|
+
7. Dark mode works consistently across all pages
|
|
271
|
+
8. All pages use BaseLayout with header/footer
|
|
272
|
+
9. Responsive on mobile and desktop
|
|
273
|
+
10. npm run build generates all static pages
|
|
274
|
+
11. No console errors on any page
|
|
275
|
+
12. User approves page structure and navigation
|
|
276
|
+
</verification>
|
|
277
|
+
|
|
278
|
+
<success_criteria>
|
|
279
|
+
- Homepage displays recent posts and hero content
|
|
280
|
+
- Blog index shows complete post listing
|
|
281
|
+
- About page contains author information
|
|
282
|
+
- Tag filtering functional (click tag → see filtered posts)
|
|
283
|
+
- All navigation links work correctly
|
|
284
|
+
- Build generates all static pages
|
|
285
|
+
- Consistent styling and dark mode across all pages
|
|
286
|
+
- User approves overall page structure and UX
|
|
287
|
+
</success_criteria>
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
cluster: james-weatherhead-blog
|
|
3
|
+
block: 05
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 3
|
|
6
|
+
depends_on: [01, 03]
|
|
7
|
+
files_modified: [src/pages/rss.xml.ts, src/utils/rss.ts]
|
|
8
|
+
autonomous: true
|
|
9
|
+
|
|
10
|
+
must_haves:
|
|
11
|
+
truths:
|
|
12
|
+
- "RSS feed is accessible at /rss.xml"
|
|
13
|
+
- "Feed contains all published blog posts"
|
|
14
|
+
- "Feed validates against RSS 2.0 specification"
|
|
15
|
+
artifacts:
|
|
16
|
+
- path: "src/pages/rss.xml.ts"
|
|
17
|
+
provides: "RSS feed endpoint"
|
|
18
|
+
exports: ["GET"]
|
|
19
|
+
- path: "src/utils/rss.ts"
|
|
20
|
+
provides: "RSS generation utility functions"
|
|
21
|
+
min_lines: 20
|
|
22
|
+
key_links:
|
|
23
|
+
- from: "rss.xml.ts"
|
|
24
|
+
to: "@astrojs/rss"
|
|
25
|
+
via: "rss() function import"
|
|
26
|
+
pattern: "import.*@astrojs/rss"
|
|
27
|
+
- from: "rss.xml.ts"
|
|
28
|
+
to: "getCollection('blog')"
|
|
29
|
+
via: "fetch posts for feed"
|
|
30
|
+
pattern: "getCollection\\('blog'\\)"
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
<objective>
|
|
34
|
+
Implement RSS feed generation for blog posts using @astrojs/rss.
|
|
35
|
+
|
|
36
|
+
Purpose: Provide RSS feed for readers who prefer feed readers (Feedly, NewsBlur, etc.).
|
|
37
|
+
Output: Valid RSS 2.0 feed at /rss.xml with all published posts.
|
|
38
|
+
</objective>
|
|
39
|
+
|
|
40
|
+
<context>
|
|
41
|
+
Dependencies:
|
|
42
|
+
- Block 01: @astrojs/rss installed during project setup
|
|
43
|
+
- Block 03: Blog content collection configured
|
|
44
|
+
|
|
45
|
+
This block adds RSS feed support - a standard feature for technical blogs. Requirements explicitly mention "RSS feed" as a feature.
|
|
46
|
+
|
|
47
|
+
RSS feeds are static XML files generated at build time. Astro's @astrojs/rss integration makes this straightforward - no complex logic needed.
|
|
48
|
+
|
|
49
|
+
This block runs in parallel with Block 04 (wave 3) since there's no file overlap:
|
|
50
|
+
- Block 04 modifies: pages (index, blog, about, tags)
|
|
51
|
+
- Block 05 modifies: rss.xml.ts, utils/rss.ts
|
|
52
|
+
</context>
|
|
53
|
+
|
|
54
|
+
<threads>
|
|
55
|
+
|
|
56
|
+
<thread type="auto">
|
|
57
|
+
<name>Thread 1: Create RSS feed endpoint</name>
|
|
58
|
+
<files>src/pages/rss.xml.ts</files>
|
|
59
|
+
<action>
|
|
60
|
+
Create RSS feed using @astrojs/rss integration:
|
|
61
|
+
|
|
62
|
+
1. Create src/pages/rss.xml.ts (note: .ts not .astro for API route)
|
|
63
|
+
|
|
64
|
+
2. Import dependencies:
|
|
65
|
+
```ts
|
|
66
|
+
import rss from '@astrojs/rss';
|
|
67
|
+
import { getCollection } from 'astro:content';
|
|
68
|
+
import type { APIContext } from 'astro';
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
3. Export GET function:
|
|
72
|
+
```ts
|
|
73
|
+
export async function GET(context: APIContext) {
|
|
74
|
+
const posts = await getCollection('blog', ({ data }) => !data.draft);
|
|
75
|
+
const sortedPosts = posts.sort(
|
|
76
|
+
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return rss({
|
|
80
|
+
title: 'James Weatherhead Blog',
|
|
81
|
+
description: 'Technical writing on web development, DevOps, and software engineering',
|
|
82
|
+
site: context.site || 'https://jamesweatherhead.com',
|
|
83
|
+
items: sortedPosts.map(post => ({
|
|
84
|
+
title: post.data.title,
|
|
85
|
+
description: post.data.description,
|
|
86
|
+
pubDate: post.data.pubDate,
|
|
87
|
+
link: `/blog/${post.slug}/`,
|
|
88
|
+
categories: post.data.tags,
|
|
89
|
+
})),
|
|
90
|
+
customData: '<language>en-us</language>',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
4. Ensure astro.config.mjs has site URL set (required for RSS):
|
|
96
|
+
- If not set, use placeholder: https://jamesweatherhead.com
|
|
97
|
+
|
|
98
|
+
AVOID: Do not add full content to RSS (use description only for preview). Do not add custom XML transformations (use @astrojs/rss defaults). WHY: Full content RSS increases bandwidth and complexity; previews encourage click-through to site.
|
|
99
|
+
</action>
|
|
100
|
+
<verify>
|
|
101
|
+
Run: npm run build
|
|
102
|
+
Check: dist/rss.xml exists
|
|
103
|
+
Run: npm run preview
|
|
104
|
+
Visit: http://localhost:4321/rss.xml
|
|
105
|
+
Check: XML renders in browser with all posts
|
|
106
|
+
Validate: Use https://validator.w3.org/feed/ or RSS validator
|
|
107
|
+
</verify>
|
|
108
|
+
<done>
|
|
109
|
+
- rss.xml.ts exports GET function returning RSS feed
|
|
110
|
+
- Feed includes all non-draft posts sorted by date
|
|
111
|
+
- Each item has title, description, pubDate, link, categories (tags)
|
|
112
|
+
- site URL configured in astro.config.mjs
|
|
113
|
+
- RSS feed accessible at /rss.xml
|
|
114
|
+
</done>
|
|
115
|
+
</thread>
|
|
116
|
+
|
|
117
|
+
<thread type="auto">
|
|
118
|
+
<name>Thread 2: Create RSS utility functions (optional helpers)</name>
|
|
119
|
+
<files>src/utils/rss.ts</files>
|
|
120
|
+
<action>
|
|
121
|
+
Create optional utility functions for RSS generation:
|
|
122
|
+
|
|
123
|
+
1. Create src/utils/rss.ts
|
|
124
|
+
|
|
125
|
+
2. Add helper functions:
|
|
126
|
+
```ts
|
|
127
|
+
import type { CollectionEntry } from 'astro:content';
|
|
128
|
+
|
|
129
|
+
export function formatPostForRSS(post: CollectionEntry<'blog'>) {
|
|
130
|
+
return {
|
|
131
|
+
title: post.data.title,
|
|
132
|
+
description: post.data.description,
|
|
133
|
+
pubDate: post.data.pubDate,
|
|
134
|
+
link: `/blog/${post.slug}/`,
|
|
135
|
+
categories: post.data.tags,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function sortPostsByDate(posts: CollectionEntry<'blog'>[]) {
|
|
140
|
+
return posts.sort(
|
|
141
|
+
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
3. Refactor rss.xml.ts to use these utilities (DRY principle)
|
|
147
|
+
|
|
148
|
+
Note: This thread is optional but recommended for cleaner code. If time-constrained, inline logic in rss.xml.ts is acceptable.
|
|
149
|
+
|
|
150
|
+
AVOID: Do not add XML parsing utilities (use @astrojs/rss). Do not add feed caching (Astro handles this). WHY: @astrojs/rss provides all RSS functionality; additional abstractions add unnecessary complexity.
|
|
151
|
+
</action>
|
|
152
|
+
<verify>
|
|
153
|
+
Check: src/utils/rss.ts exports helper functions
|
|
154
|
+
Check: rss.xml.ts imports and uses utilities
|
|
155
|
+
Run: npm run build (should succeed)
|
|
156
|
+
Check: dist/rss.xml unchanged (same output, cleaner code)
|
|
157
|
+
</verify>
|
|
158
|
+
<done>
|
|
159
|
+
- src/utils/rss.ts created with helper functions
|
|
160
|
+
- formatPostForRSS converts post to RSS item format
|
|
161
|
+
- sortPostsByDate sorts posts by date descending
|
|
162
|
+
- rss.xml.ts refactored to use utilities
|
|
163
|
+
- Build output identical (cleaner code, same result)
|
|
164
|
+
</done>
|
|
165
|
+
</thread>
|
|
166
|
+
|
|
167
|
+
<thread type="auto">
|
|
168
|
+
<name>Thread 3: Add RSS autodiscovery link</name>
|
|
169
|
+
<files>src/layouts/BaseLayout.astro</files>
|
|
170
|
+
<action>
|
|
171
|
+
Add RSS feed autodiscovery to site header:
|
|
172
|
+
|
|
173
|
+
1. Open src/layouts/BaseLayout.astro (created in Block 02)
|
|
174
|
+
|
|
175
|
+
2. In the <head> section, add autodiscovery link:
|
|
176
|
+
```html
|
|
177
|
+
<link
|
|
178
|
+
rel="alternate"
|
|
179
|
+
type="application/rss+xml"
|
|
180
|
+
title="James Weatherhead Blog"
|
|
181
|
+
href="/rss.xml"
|
|
182
|
+
/>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
3. This allows:
|
|
186
|
+
- Browsers to detect RSS feed (shows RSS icon in address bar)
|
|
187
|
+
- Feed readers to auto-discover feed
|
|
188
|
+
- Standard SEO best practice for blogs
|
|
189
|
+
|
|
190
|
+
4. Optional: Add visible RSS link in Footer component:
|
|
191
|
+
- Icon or text link: "RSS Feed"
|
|
192
|
+
- Links to /rss.xml
|
|
193
|
+
|
|
194
|
+
AVOID: Do not add Atom feed (RSS 2.0 sufficient). Do not add JSON Feed (RSS standard more widely supported). WHY: RSS 2.0 is universal standard; additional feed formats add maintenance overhead without significant benefit.
|
|
195
|
+
</action>
|
|
196
|
+
<verify>
|
|
197
|
+
Run: npm run dev
|
|
198
|
+
Visit: http://localhost:4321
|
|
199
|
+
Check: View page source - <link rel="alternate" type="application/rss+xml"> in <head>
|
|
200
|
+
Check: Browser RSS icon appears (if browser supports)
|
|
201
|
+
Check: Footer has RSS link (if added)
|
|
202
|
+
</verify>
|
|
203
|
+
<done>
|
|
204
|
+
- BaseLayout.astro has RSS autodiscovery link in <head>
|
|
205
|
+
- RSS feed detectable by browsers and feed readers
|
|
206
|
+
- Optional: Footer contains visible RSS link
|
|
207
|
+
- Feed properly advertised to users
|
|
208
|
+
</done>
|
|
209
|
+
</thread>
|
|
210
|
+
|
|
211
|
+
</threads>
|
|
212
|
+
|
|
213
|
+
<verification>
|
|
214
|
+
Complete verification checklist:
|
|
215
|
+
1. src/pages/rss.xml.ts exports GET function
|
|
216
|
+
2. RSS feed includes all non-draft posts
|
|
217
|
+
3. Posts sorted by date descending (newest first)
|
|
218
|
+
4. Each item has title, description, pubDate, link, categories
|
|
219
|
+
5. Feed validates against RSS 2.0 spec (use validator)
|
|
220
|
+
6. /rss.xml accessible after build
|
|
221
|
+
7. BaseLayout.astro has autodiscovery link in <head>
|
|
222
|
+
8. Optional: Footer has visible RSS link
|
|
223
|
+
9. npm run build generates dist/rss.xml
|
|
224
|
+
10. Feed loads in feed reader (test with Feedly or similar)
|
|
225
|
+
</verification>
|
|
226
|
+
|
|
227
|
+
<success_criteria>
|
|
228
|
+
- RSS feed accessible at /rss.xml
|
|
229
|
+
- Feed contains all published blog posts
|
|
230
|
+
- Feed validates against RSS 2.0 specification
|
|
231
|
+
- Autodiscovery link present in site <head>
|
|
232
|
+
- Feed items have complete metadata (title, description, date, link, tags)
|
|
233
|
+
- Build generates static RSS XML file
|
|
234
|
+
- Feed works in standard feed readers
|
|
235
|
+
</success_criteria>
|