Skip to content

Fix search#875

Merged
tannerlinsley merged 10 commits intomainfrom
fix-search
Apr 30, 2026
Merged

Fix search#875
tannerlinsley merged 10 commits intomainfrom
fix-search

Conversation

@LadyBluenotes
Copy link
Copy Markdown
Member

@LadyBluenotes LadyBluenotes commented Apr 30, 2026

Summary by CodeRabbit

  • New Features

    • Search results now support framework-specific content and anchored direct links
    • Improved filter options in search with internal computation
    • Added alternate language variants in search results metadata
  • Bug Fixes

    • Enhanced search result rendering for better accuracy and link handling
  • Improvements

    • Refined sitemap generation with smarter page classification across libraries
    • Updated library configurations for consistent documentation coverage

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 30, 2026

Deploy Preview for tanstack ready!

Name Link
🔨 Latest commit 5b18585
🔍 Latest deploy log https://app.netlify.com/projects/tanstack/deploys/69f2aba916ead80008e251d5
😎 Deploy Preview https://deploy-preview-875--tanstack.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 34 (🔴 down 30 from production)
Accessibility: 90 (no change from production)
Best Practices: 83 (🔴 down 9 from production)
SEO: 97 (no change from production)
PWA: 70 (no change from production)
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

This pull request adds framework-aware search and documentation features to the documentation portal. It introduces framework extraction from markdown, implements framework persistence in search results, refactors search filtering logic, updates sitemap configuration and properties across libraries, and adds framework-variant alternate links for SEO. Includes comprehensive test coverage for new utilities.

Changes

Cohort / File(s) Summary
UI Component Updates
src/components/Dropdown.tsx, src/components/SearchModal.tsx
Dropdown now supports optional portal prop to disable portal rendering. SearchModal refactored to replace Algolia facet-based menus with internally computed filter lists, adds framework persistence logic, uses urlWithAnchor for links, and renders dropdown content without portal.
Library Configuration
src/libraries/types.ts, src/libraries/libraries.ts
Type definition updated: sitemap.includeTopLevelDocsPages renamed to sitemap.includeDocsPages. Configuration updated across multiple libraries with consistent sitemap blocks and badgeTextStyle styling additions.
Documentation & Markdown Processing
src/utils/docs.functions.ts, src/utils/markdown/filterFrameworkContent.ts, src/utils/documents.server.ts
New extractFrameworksFromMarkdown utility parses framework-specific and package-manager sections from markdown. fetchDocsManifest refactored into helper; docs fetching now extracts and forwards frameworks. shouldUseLocalDocsFiles exported for broader access.
Search & Framework Utilities
src/utils/searchRecords.ts
New utilities added: SearchHitFrameworkContext type, hasFrameworkPath detector, and shouldPersistFrameworkForHit logic for managing framework selection persistence in search results.
SEO & Routing
src/routes/$libraryId/$version.docs.$.tsx, src/utils/sitemap.ts
Route head now generates framework-variant alternate links for markdown documentation. Sitemap filtering refactored to use high-value slug classifier instead of fixed depth; property reference updated to new includeDocsPages naming.
Tests
tests/filter-framework-content.test.ts, tests/search-records.test.ts
New test files validate framework extraction from markdown (blocks and tabs sections) and search hit framework persistence logic across multiple URL/framework/routeStyle combinations.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SearchModal
    participant FilterLogic as Filter Logic
    participant SearchAPI as Search API
    participant DataProcessor as Data Processor
    participant DropdownContent as Dropdown Content

    User->>SearchModal: Open search & type query
    SearchModal->>FilterLogic: Build search filters via buildSearchFilters
    FilterLogic->>FilterLogic: Compute available options from library visibility
    FilterLogic->>SearchAPI: Configure.filters (internally computed)
    
    SearchAPI-->>SearchModal: Return search results with framework/urlWithAnchor
    SearchModal->>DataProcessor: Process hit (extract hit.framework, hit.urlWithAnchor)
    DataProcessor->>DataProcessor: Check shouldPersistFrameworkForHit
    
    alt Framework should persist
        DataProcessor->>SearchModal: Preserve framework preference
    else Framework should not persist
        DataProcessor->>SearchModal: Clear selectedFramework
    end
    
    SearchModal->>DropdownContent: Render with portal={false}
    DropdownContent-->>User: Display filtered options without portal
    
    User->>SearchModal: Click search result
    SearchModal->>SearchModal: Close modal (framework persisted if applicable)
Loading
sequenceDiagram
    participant RouteHandler as Route Handler
    participant DocsFunctions as Docs Functions
    participant MarkdownParser as Markdown Parser
    participant ManifestBuilder as Manifest Builder
    participant HeadGenerator as Head Generator

    RouteHandler->>DocsFunctions: fetchDocsPage(libraryId, version, path)
    DocsFunctions->>ManifestBuilder: buildDocsManifest (if local mode)
    ManifestBuilder->>MarkdownParser: Parse markdown files
    MarkdownParser->>MarkdownParser: Extract frameworks via extractFrameworksFromMarkdown
    MarkdownParser-->>ManifestBuilder: Return manifest with framework data
    
    ManifestBuilder-->>DocsFunctions: Manifest ready
    DocsFunctions->>DocsFunctions: Extract frameworks from markdown content
    DocsFunctions-->>RouteHandler: Return docs page + frameworks array
    
    RouteHandler->>HeadGenerator: Generate head with alternate links
    HeadGenerator->>HeadGenerator: Create link rel=alternate for each framework variant
    HeadGenerator-->>RouteHandler: Return meta + links for SEO
    
    RouteHandler-->>RouteHandler: Render docs page with alternate links in head
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Frameworks dance through markdown lines,
Search results gleam in filtered designs,
Portal gates now open wide,
Alternate links for SEO pride,
Libraries configured, frameworks extracted,
Search experience now perfectly crafted! 🌟

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fix search' is vague and generic, using non-descriptive language that doesn't convey meaningful information about the substantial changes across multiple components and utilities. Consider a more specific title that highlights the main focus, such as 'Refactor search modal with framework-aware filtering and portal control' or 'Improve search with framework-aware content and sitemap updates'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-search

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.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/filter-framework-content.test.ts (1)

3-9: 💤 Low value

Consider using Node.js built-in assert for consistency.

The sibling test file tests/search-records.test.ts uses node:assert/strict. Using the same assertion library would improve consistency across the test suite.

Suggested change
-function assertEqual(actual: unknown, expected: unknown, message: string) {
-  const actualJson = JSON.stringify(actual)
-  const expectedJson = JSON.stringify(expected)
-  if (actualJson !== expectedJson) {
-    throw new Error(`${message}: expected ${expectedJson}, got ${actualJson}`)
-  }
-}
+import assert from 'node:assert/strict'

Then use assert.deepEqual(actual, expected, message) for assertions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/filter-framework-content.test.ts` around lines 3 - 9, The test defines
a custom helper function assertEqual which duplicates Node's assertion behavior;
replace it with the built-in strict assert module by importing from
"node:assert/strict" and remove the custom function assertEqual, then update
test usages to call assert.deepEqual(actual, expected, message) (or
assert.deepStrictEqual if preferred) so the test suite matches the sibling
file's assertion style.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/routes/`$libraryId/$version.docs.$.tsx:
- Around line 71-76: frameworkVariantLinks constructs alternate links that
append ".md" to the docs path, which causes the loader to resolve to ".md.md"
(or "/docs/.md") and break routes; update the href template in the
frameworkVariantLinks mapping to stop appending ".md" (use
`/\${libraryId}/\${version}/docs/\${docsPath}?framework=\${framework}` or ensure
docsPath is normalized so no ".md" is added) so the loader receives the raw
docsPath it expects and alternate links point to existing routes.

In `@src/utils/docs.functions.ts`:
- Around line 158-159: frontMatter.data.redirectFrom may be a string and the
current for...of loops over characters; coerce it to an array before iterating
(e.g., create a redirectFromList from frontMatter.data.redirectFrom using
Array.isArray check or wrapping non-arrays) and filter out falsy/non-string
entries, then iterate redirectFromList and call normalizeDocsRedirectPath for
each; update the loop that currently references redirectFrom,
normalizeDocsRedirectPath, and normalizedRedirect to use the new normalized list
so single-string frontmatter yields one redirect rather than per-character
redirects.
- Around line 150-154: The fetchRepoFile call can throw and should not abort the
whole manifest build; wrap the await fetchRepoFile(repo, branch, node.path) in a
try/catch inside the same loop (where you currently check if (!file) { continue
}) so that on any recoverable GitHub error you log or warn and continue to the
next node instead of letting the exception propagate; keep the existing
null/undefined check for file and reference fetchRepoFile, node.path and the
continue behavior when skipping the problematic file.

---

Nitpick comments:
In `@tests/filter-framework-content.test.ts`:
- Around line 3-9: The test defines a custom helper function assertEqual which
duplicates Node's assertion behavior; replace it with the built-in strict assert
module by importing from "node:assert/strict" and remove the custom function
assertEqual, then update test usages to call assert.deepEqual(actual, expected,
message) (or assert.deepStrictEqual if preferred) so the test suite matches the
sibling file's assertion style.
🪄 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: 98295c36-c9c9-4f77-9cd7-8a7b26d6ca74

📥 Commits

Reviewing files that changed from the base of the PR and between 092876e and 5b18585.

📒 Files selected for processing (12)
  • src/components/Dropdown.tsx
  • src/components/SearchModal.tsx
  • src/libraries/libraries.ts
  • src/libraries/types.ts
  • src/routes/$libraryId/$version.docs.$.tsx
  • src/utils/docs.functions.ts
  • src/utils/documents.server.ts
  • src/utils/markdown/filterFrameworkContent.ts
  • src/utils/searchRecords.ts
  • src/utils/sitemap.ts
  • tests/filter-framework-content.test.ts
  • tests/search-records.test.ts

Comment on lines +71 to +76
const frameworkVariantLinks = (loaderData?.frameworks ?? []).map(
(framework) => ({
rel: 'alternate',
type: 'text/markdown',
href: `/${libraryId}/${version}/docs/${docsPath}.md?framework=${framework}`,
}),
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 | ⚡ Quick win

Alternate framework links currently point to non-existent docs routes.

Line 75 appends .md to the URL path, but this route’s loader resolves files by appending .md again. That can produce ${docsPath}.md.md (or /docs/.md at docs root) and break these alternates.

Proposed fix
-    const frameworkVariantLinks = (loaderData?.frameworks ?? []).map(
+    const docsPathSuffix = docsPath ? `/${docsPath}` : ''
+    const frameworkVariantLinks = (loaderData?.frameworks ?? []).map(
       (framework) => ({
         rel: 'alternate',
         type: 'text/markdown',
-        href: `/${libraryId}/${version}/docs/${docsPath}.md?framework=${framework}`,
+        href: `/${libraryId}/${version}/docs${docsPathSuffix}?framework=${encodeURIComponent(framework)}`,
       }),
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/`$libraryId/$version.docs.$.tsx around lines 71 - 76,
frameworkVariantLinks constructs alternate links that append ".md" to the docs
path, which causes the loader to resolve to ".md.md" (or "/docs/.md") and break
routes; update the href template in the frameworkVariantLinks mapping to stop
appending ".md" (use
`/\${libraryId}/\${version}/docs/\${docsPath}?framework=\${framework}` or ensure
docsPath is normalized so no ".md" is added) so the loader receives the raw
docsPath it expects and alternate links point to existing routes.

Comment on lines +150 to +154
const file = await fetchRepoFile(repo, branch, node.path)

if (!file) {
continue
}
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 | ⚡ Quick win

Single-file fetch failures can abort the entire manifest build.

Line 150 can throw, and currently one recoverable GitHub error takes down the full manifest generation path. This should skip that file instead of failing the whole build.

Proposed fix
-    const file = await fetchRepoFile(repo, branch, node.path)
+    let file: string | undefined
+    try {
+      file = await fetchRepoFile(repo, branch, node.path)
+    } catch (error) {
+      if (!isRecoverableGitHubContentError(error)) {
+        throw error
+      }
+      continue
+    }

     if (!file) {
       continue
     }
📝 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 file = await fetchRepoFile(repo, branch, node.path)
if (!file) {
continue
}
let file: string | undefined
try {
file = await fetchRepoFile(repo, branch, node.path)
} catch (error) {
if (!isRecoverableGitHubContentError(error)) {
throw error
}
continue
}
if (!file) {
continue
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/docs.functions.ts` around lines 150 - 154, The fetchRepoFile call
can throw and should not abort the whole manifest build; wrap the await
fetchRepoFile(repo, branch, node.path) in a try/catch inside the same loop
(where you currently check if (!file) { continue }) so that on any recoverable
GitHub error you log or warn and continue to the next node instead of letting
the exception propagate; keep the existing null/undefined check for file and
reference fetchRepoFile, node.path and the continue behavior when skipping the
problematic file.

Comment on lines +158 to +159
for (const redirectFrom of frontMatter.data.redirectFrom ?? []) {
const normalizedRedirect = normalizeDocsRedirectPath(
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 | ⚡ Quick win

redirectFrom should be normalized before iteration.

Line 158 assumes redirectFrom is an array. If frontmatter provides a single string, the loop iterates characters and generates invalid redirects.

Proposed fix
-    for (const redirectFrom of frontMatter.data.redirectFrom ?? []) {
+    const redirectFromValues = Array.isArray(frontMatter.data.redirectFrom)
+      ? frontMatter.data.redirectFrom
+      : typeof frontMatter.data.redirectFrom === 'string'
+        ? [frontMatter.data.redirectFrom]
+        : []
+
+    for (const redirectFrom of redirectFromValues) {
📝 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
for (const redirectFrom of frontMatter.data.redirectFrom ?? []) {
const normalizedRedirect = normalizeDocsRedirectPath(
const redirectFromValues = Array.isArray(frontMatter.data.redirectFrom)
? frontMatter.data.redirectFrom
: typeof frontMatter.data.redirectFrom === 'string'
? [frontMatter.data.redirectFrom]
: []
for (const redirectFrom of redirectFromValues) {
const normalizedRedirect = normalizeDocsRedirectPath(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/docs.functions.ts` around lines 158 - 159,
frontMatter.data.redirectFrom may be a string and the current for...of loops
over characters; coerce it to an array before iterating (e.g., create a
redirectFromList from frontMatter.data.redirectFrom using Array.isArray check or
wrapping non-arrays) and filter out falsy/non-string entries, then iterate
redirectFromList and call normalizeDocsRedirectPath for each; update the loop
that currently references redirectFrom, normalizeDocsRedirectPath, and
normalizedRedirect to use the new normalized list so single-string frontmatter
yields one redirect rather than per-character redirects.

@tannerlinsley tannerlinsley merged commit 70d2fcd into main Apr 30, 2026
8 checks passed
@tannerlinsley tannerlinsley deleted the fix-search branch April 30, 2026 03:29
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.

2 participants