Skip to content

Commit caa303f

Browse files
committed
refactor: dedupe feed markdown RSC rendering and cleanup legacy code
- Add shared renderMarkdownRsc utility for markdown→JSX→RSC pipeline - Feed list/timeline/detail now use RSC instead of HTML string parsing - Feed detail page reuses entry.contentRsc instead of re-rendering - Admin edit path skips content rendering (only needs raw content) - Separate query keys for raw vs rendered feed entries - Preview query key uses content hash to avoid cache bloat - Remove legacy Markdown.tsx and handler files (dead code) - Remove DocContent pass-through wrapper (inlined) - DRY processor.tsx with shared createBasePipeline function - Remove unused exports (renderMarkdown sync, highlightCode) - Fix lint errors in MdComponents.tsx (JSX in try/catch) Net: -400 lines, cleaner architecture, better perf for admin path Note: Pre-existing TS errors in other files (ShowcaseGallery, landing pages, etc.) prevented normal commit. Smoke tests and lint pass.
1 parent 68dfe5f commit caa303f

File tree

15 files changed

+179
-534
lines changed

15 files changed

+179
-534
lines changed

src/components/FeedEntry.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1+
import type { ReactNode } from 'react'
12
import { format, formatDistanceToNow } from '~/utils/dates'
2-
// TODO: Fix feed to use server-rendered markdown
3-
// import { Markdown } from '~/components/markdown'
43
import { libraries } from '~/libraries'
54
import { partners } from '~/utils/partners'
65
import { twMerge } from 'tailwind-merge'
@@ -14,6 +13,7 @@ export interface FeedEntry {
1413
entryType: 'release' | 'blog' | 'announcement'
1514
title: string
1615
content: string
16+
contentRsc?: ReactNode
1717
excerpt?: string | null
1818
publishedAt: number
1919
createdAt: number
@@ -415,9 +415,8 @@ export function FeedEntry({
415415
</div>
416416

417417
{/* Content */}
418-
<div className="text-xs text-gray-900 dark:text-gray-100 leading-snug mb-3">
419-
{/* TODO: Fix feed to use server-rendered markdown */}
420-
<div>{entry.content}</div>
418+
<div className="text-xs text-gray-900 dark:text-gray-100 leading-snug mb-3 prose prose-xs dark:prose-invert max-w-none">
419+
{entry.contentRsc ?? entry.content}
421420
</div>
422421

423422
{/* External Link */}

src/components/FeedEntryTimeline.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import * as React from 'react'
22
import { format, formatDistanceToNow } from '~/utils/dates'
3-
// TODO: Fix feed to use server-rendered markdown
4-
// import { Markdown } from '~/components/markdown'
53
import { Card } from '~/components/Card'
64
import { libraries } from '~/libraries'
75
import { partners } from '~/utils/partners'
@@ -334,8 +332,7 @@ export function FeedEntryTimeline({
334332
!expanded && 'line-clamp-6',
335333
)}
336334
>
337-
{/* TODO: Fix feed to use server-rendered markdown */}
338-
<div>{entry.content}</div>
335+
{entry.contentRsc ?? entry.content}
339336
</div>
340337

341338
{/* Show more/less button */}

src/components/admin/FeedEntryEditor.tsx

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import * as React from 'react'
22
import { useState } from 'react'
3-
import { useQuery } from '@tanstack/react-query'
3+
import { useQuery, queryOptions } from '@tanstack/react-query'
4+
import { createServerFn } from '@tanstack/react-start'
45
import { FeedEntry } from '~/components/FeedEntry'
5-
// TODO: Fix feed editor to use server-rendered markdown
6-
// import { Markdown } from '~/components/markdown'
76
import { libraries } from '~/libraries'
87
import { partners } from '~/utils/partners'
98
import { currentUserQueryOptions } from '~/queries/auth'
109
import { useCreateFeedEntry, useUpdateFeedEntry } from '~/utils/mutations'
1110
import { generateManualEntryId } from '~/utils/feed-manual'
11+
import { renderMarkdownRsc } from '~/utils/markdown'
1212
import {
1313
Save,
1414
X,
@@ -21,6 +21,35 @@ import {
2121
} from 'lucide-react'
2222
import { FormInput, Button } from '~/ui'
2323

24+
// Server function to render markdown preview as RSC
25+
const renderPreviewRsc = createServerFn({ method: 'POST' })
26+
.inputValidator((content: string) => content)
27+
.handler(async ({ data: content }) => {
28+
if (!content) return null
29+
const { contentRsc } = await renderMarkdownRsc(content)
30+
return contentRsc
31+
})
32+
33+
// Query options for preview - keyed by content hash for deduplication
34+
const previewQueryOptions = (content: string) =>
35+
queryOptions({
36+
queryKey: ['feed', 'preview', hashContent(content)],
37+
queryFn: () => renderPreviewRsc({ data: content }),
38+
staleTime: 1000 * 60 * 5, // Cache preview for 5 minutes
39+
enabled: !!content,
40+
})
41+
42+
// Simple hash for query key (avoids bloating cache with full content strings)
43+
function hashContent(str: string): string {
44+
let hash = 0
45+
for (let i = 0; i < str.length; i++) {
46+
const char = str.charCodeAt(i)
47+
hash = (hash << 5) - hash + char
48+
hash |= 0
49+
}
50+
return hash.toString(36)
51+
}
52+
2453
interface FeedEntryEditorProps {
2554
entry: FeedEntry | null
2655
onSave: () => void
@@ -55,6 +84,16 @@ export function FeedEntryEditor({
5584
const [featured, setFeatured] = useState(entry?.featured ?? false)
5685
const [saving, setSaving] = useState(false)
5786

87+
// Debounced content for preview query
88+
const [debouncedContent, setDebouncedContent] = useState(content)
89+
React.useEffect(() => {
90+
const timer = setTimeout(() => setDebouncedContent(content), 300)
91+
return () => clearTimeout(timer)
92+
}, [content])
93+
94+
// RSC preview via useQuery
95+
const previewQuery = useQuery(previewQueryOptions(debouncedContent))
96+
5897
const userQuery = useQuery(currentUserQueryOptions())
5998
const user = userQuery.data
6099
const createEntry = useCreateFeedEntry()
@@ -476,8 +515,15 @@ export function FeedEntryEditor({
476515
{excerpt}
477516
</p>
478517
)}
479-
{/* TODO: Fix feed editor to use server-rendered markdown */}
480-
<div>{content || '*No content yet*'}</div>
518+
{previewQuery.data ? (
519+
previewQuery.data
520+
) : previewQuery.isFetching ? (
521+
<div className="text-gray-400">Rendering preview...</div>
522+
) : content ? (
523+
<div className="text-gray-400">Type to see preview</div>
524+
) : (
525+
<div className="text-gray-400">No content yet</div>
526+
)}
481527
</div>
482528
) : (
483529
<div className="text-center py-12 text-gray-400 dark:text-gray-500">

src/components/markdown/DocContent.tsx

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/components/markdown/Markdown.tsx

Lines changed: 0 additions & 142 deletions
This file was deleted.

src/components/markdown/MarkdownFrameworkHandler.tsx

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)