feat: add tkdodo past query and router articles to blog feed - #1000
Conversation
📝 WalkthroughWalkthroughThe blog system now aggregates internal and external posts, normalizes authors, supports library and text filtering, applies shared caching, and renders external posts with secure new-tab links. ChangesExternal Blog Posts Integration
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant BlogRoute
participant BlogServerFunctions
participant ExternalBlogPosts
participant StandardSite
participant BlogCard
BlogRoute->>BlogServerFunctions: load merged blog posts
BlogServerFunctions->>ExternalBlogPosts: fetch external posts
ExternalBlogPosts->>StandardSite: request paginated records
StandardSite-->>ExternalBlogPosts: return standard-site records
ExternalBlogPosts-->>BlogServerFunctions: return BlogCardPost[]
BlogServerFunctions-->>BlogRoute: return sorted posts
BlogRoute->>BlogCard: render each post
BlogCard-->>BlogRoute: use external anchor or internal link
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
scripts/scrape-tkdodo-blog-images.ts (1)
277-283: ⚡ Quick winKeep the batch running when one post fetch fails.
A single network/parser error currently aborts the whole run. Wrap each iteration in
try/catchso remaining posts still produce mappings.Suggested fix
for (const item of items) { - const entry = await scrapePostImage(item) - - if (entry) { - entries.push(entry) - } + try { + const entry = await scrapePostImage(item) + if (entry) { + entries.push(entry) + } + } catch (error) { + console.warn(`[skip] ${getExternalPostSlug(item)}: failed to scrape`, error) + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/scrape-tkdodo-blog-images.ts` around lines 277 - 283, The loop iterating over items does not handle errors from the scrapePostImage function call, causing any network or parser error to abort the entire batch. Wrap the scrapePostImage function call and the entry push logic inside a try/catch block within the for loop. In the catch block, log the error with context (such as the current item being processed) but allow the loop to continue processing the remaining items instead of crashing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/utils/external-blog-posts.server.ts`:
- Around line 191-214: The externalUrl assignment uses feedItem.link directly
without validating the URL scheme, which is a security risk. Before the return
statement that creates the post object (after the getExternalPostSlug call), add
validation to ensure feedItem.link uses a safe scheme (http: or https:). If the
URL does not match these schemes, return an empty array to drop the invalid
item. This validation should be done by checking the URL scheme before passing
feedItem.link to addSearchParams.
---
Nitpick comments:
In `@scripts/scrape-tkdodo-blog-images.ts`:
- Around line 277-283: The loop iterating over items does not handle errors from
the scrapePostImage function call, causing any network or parser error to abort
the entire batch. Wrap the scrapePostImage function call and the entry push
logic inside a try/catch block within the for loop. In the catch block, log the
error with context (such as the current item being processed) but allow the loop
to continue processing the remaining items instead of crashing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 281cd017-bfee-4b7e-9642-eea304dee620
⛔ Files ignored due to path filters (36)
public/blog-assets/tkdodosblog/automatic-query-invalidation-after-mutations.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/breaking-react-querys-api-on-purpose.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/concurrent-optimistic-updates-in-react-query.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/context-inheritance-in-tan-stack-router.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/creating-query-abstractions.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/effective-react-query-keys.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/how-infinite-queries-work.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/inside-react-query.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/leveraging-the-query-function-context.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/mastering-mutations-in-react-query.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/offline-react-query.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/placeholder-and-initial-data-in-react-query.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/practical-react-query.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/react-query-and-forms.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-and-react-context.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-and-type-script.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-api-design-lessons-learned.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/react-query-as-a-state-manager.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-data-transformations.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-error-handling.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-fa-qs.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-meets-react-router.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-render-optimizations.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/react-query-selectors-supercharged.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/react-query-the-bad-parts.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/seeding-the-query-cache.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/status-checks-in-react-query.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/tan-stack-router-and-query.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/testing-react-query.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/the-beauty-of-tan-stack-router.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/the-query-options-api.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/thinking-in-react-query.pngis excluded by!**/*.pngpublic/blog-assets/tkdodosblog/type-safe-react-query.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/using-web-sockets-with-react-query.jpegis excluded by!**/*.jpegpublic/blog-assets/tkdodosblog/why-you-want-react-query.jpgis excluded by!**/*.jpgpublic/blog-assets/tkdodosblog/you-might-not-need-react-query.jpegis excluded by!**/*.jpeg
📒 Files selected for processing (11)
public/blog-assets/tkdodosblog/tkdodosblog.webpscripts/scrape-tkdodo-blog-images.tssrc/components/BlogCard.tsxsrc/components/RecentPostsWidget.tsxsrc/components/home/HomeSocialProofSection.tsxsrc/routes/_library/$libraryId/$version.docs.blog.tsxsrc/routes/blog.index.tsxsrc/utils/blog.functions.tssrc/utils/blog.tssrc/utils/external-blog-post-images.generated.tssrc/utils/external-blog-posts.server.ts
…blogs # Conflicts: # src/components/BlogCard.tsx # src/components/home/HomeSocialProofSection.tsx # src/routes/_library/$libraryId/$version.docs.blog.tsx # src/routes/blog.index.tsx # src/utils/blog.functions.ts # src/utils/blog.ts
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
tanstack-com | 522c614 | Commit Preview URL Branch Preview URL |
Aug 03 2026, 12:56 PM |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/ds/ui/BlogPostCard.tsx`:
- Around line 58-63: Update standardSiteRecordToBlogCardPost and the canonical
URL production flow around buildStandardSiteCanonicalUrl to accept external URLs
only when their scheme is http or https; reject or omit non-HTTP(S) canonical
URLs before assigning externalUrl, so BlogPostCard never receives an unsafe href
while valid links remain unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ab9b6a34-a5c9-479b-9328-6570e266e88d
📒 Files selected for processing (4)
src/components/BlogCard.tsxsrc/components/BlogSearchFilter.tsxsrc/components/RecentPostsWidget.tsxsrc/components/ds/ui/BlogPostCard.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- src/components/BlogSearchFilter.tsx
- src/components/RecentPostsWidget.tsx
- src/components/BlogCard.tsx
| if (post.externalUrl) { | ||
| return ( | ||
| <a | ||
| href={post.externalUrl} | ||
| target="_blank" | ||
| rel="noopener noreferrer" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="src/utils/external-blog-posts.server.ts"
test -f "$file"
rg -n -C 10 \
'canonicalUrl|buildStandardSiteCanonicalUrl|externalUrl|addSearchParams|protocol' \
"$file"Repository: TanStack/tanstack.com
Length of output: 4101
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- BlogPostCard outline ---\n'
ast-grep outline src/components/ds/ui/BlogPostCard.tsx || true
printf '\n--- BlogPostCard relevant lines ---\n'
sed -n '1,110p' src/components/ds/ui/BlogPostCard.tsx | cat -n
printf '\n--- external-blog-posts relevant lines ---\n'
sed -n '180,340p' src/utils/external-blog-posts.server.ts | cat -n
printf '\n--- BlogCardPost type usages/declaration ---\n'
rg -n -C 4 'type BlogCardPost|interface BlogCardPost|externalUrl:' srcRepository: TanStack/tanstack.com
Length of output: 250
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- BlogPostCard outline ---'
ast-grep outline src/components/ds/ui/BlogPostCard.tsx || true
printf '%s\n' ''
printf '%s\n' '--- BlogPostCard relevant lines ---'
sed -n '1,110p' src/components/ds/ui/BlogPostCard.tsx | cat -n
printf '%s\n' ''
printf '%s\n' '--- external-blog-posts relevant lines ---'
sed -n '180,340p' src/utils/external-blog-posts.server.ts | cat -n
printf '%s\n' ''
printf '%s\n' '--- BlogCardPost type usages/declaration ---'
rg -n -C 4 'type BlogCardPost|interface BlogCardPost|externalUrl:' srcRepository: TanStack/tanstack.com
Length of output: 12635
Validate the URL scheme before rendering external links.
buildStandardSiteCanonicalUrl() accepts document.canonicalUrl without limiting it to http: or https:, and standardSiteRecordToBlogCardPost() stores that value as externalUrl. BlogPostCard then assigns it directly to href. Reject non-HTTP(S) canonical URLs in the producer so the unsafe scheme cannot reach React.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/ds/ui/BlogPostCard.tsx` around lines 58 - 63, Update
standardSiteRecordToBlogCardPost and the canonical URL production flow around
buildStandardSiteCanonicalUrl to accept external URLs only when their scheme is
http or https; reject or omit non-HTTP(S) canonical URLs before assigning
externalUrl, so BlogPostCard never receives an unsafe href while valid links
remain unchanged.
Source: MCP tools
Summary by CodeRabbit