From 49fd4e6dbf2de3fd2ffd9368c4bfb2dec3ca1c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Best?= Date: Fri, 26 Jun 2026 20:29:10 +0200 Subject: [PATCH 1/8] doc: version-gate unreleased content so production deploys from next nuqs.dev deploys from `master` only to keep unreleased-feature docs out of production. Gating that content inline by the published npm version lets `next` become the single deploy source and ends the cherry-pick churn between branches. --- packages/docs/content/docs/adapters.mdx | 8 +- packages/docs/content/docs/installation.mdx | 5 +- packages/docs/content/docs/options.mdx | 2 + packages/docs/mdx-components.tsx | 2 + packages/docs/src/app/styles/tweaks.css | 18 ++++ .../src/components/feature-support-matrix.tsx | 63 +++++++++--- .../docs/src/components/sidebar-footer.tsx | 23 +---- .../docs/src/components/since-version.tsx | 90 +++++++++++++++++ packages/docs/src/lib/get-llm-text.ts | 17 +++- .../docs/src/lib/published-version.test.ts | 97 +++++++++++++++++++ packages/docs/src/lib/published-version.ts | 68 +++++++++++++ .../docs/src/lib/strip-unreleased.test.ts | 86 ++++++++++++++++ packages/docs/src/lib/strip-unreleased.ts | 51 ++++++++++ 13 files changed, 493 insertions(+), 37 deletions(-) create mode 100644 packages/docs/src/components/since-version.tsx create mode 100644 packages/docs/src/lib/published-version.test.ts create mode 100644 packages/docs/src/lib/published-version.ts create mode 100644 packages/docs/src/lib/strip-unreleased.test.ts create mode 100644 packages/docs/src/lib/strip-unreleased.ts diff --git a/packages/docs/content/docs/adapters.mdx b/packages/docs/content/docs/adapters.mdx index ad9108833..9547875e4 100644 --- a/packages/docs/content/docs/adapters.mdx +++ b/packages/docs/content/docs/adapters.mdx @@ -23,7 +23,7 @@ wrapping it with a `NuqsAdapter{:ts}` context provider: - [Remix](#remix) - [React Router v6](#react-router-v6) - [React Router v7](#react-router-v7) -- [React Router v8](#react-router-v8) +- [React Router v8](#react-router-v8) - [TanStack Router](#tanstack-router) @@ -34,7 +34,9 @@ wrapping it with a `NuqsAdapter{:ts}` context provider: - [Remix](#remix) - [React Router v6](#react-router-v6) - [React Router v7](#react-router-v7) + - [React Router v8](#react-router-v8) + - [TanStack Router](#tanstack-router) @@ -247,6 +249,8 @@ export default function App() { } ``` + + ## React Router v8 [#react-router-v8] ```tsx title="app/root.tsx" @@ -275,6 +279,8 @@ export default function App() { + + ## TanStack Router [#tanstack-router] ```tsx title="src/routes/__root.tsx" diff --git a/packages/docs/content/docs/installation.mdx b/packages/docs/content/docs/installation.mdx index ef2e020db..2dc08c2b9 100644 --- a/packages/docs/content/docs/installation.mdx +++ b/packages/docs/content/docs/installation.mdx @@ -37,7 +37,7 @@ npm install nuqs - [Remix](/docs/adapters#remix): `@remix-run/react@^2` - [React Router v6](/docs/adapters#react-router-v6): `react-router-dom@^6` - [React Router v7](/docs/adapters#react-router-v7): `react-router@^7` -- [React Router v8](/docs/adapters#react-router-v8): `react-router@^8` +- [React Router v8](/docs/adapters#react-router-v8): `react-router@^8` - [TanStack Router](/docs/adapters#tanstack-router): `@tanstack/react-router@^1` @@ -47,6 +47,9 @@ npm install nuqs - [Remix](/docs/adapters#remix): `@remix-run/react@^2` - [React Router v6](/docs/adapters#react-router-v6): `react-router-dom@^6` - [React Router v7](/docs/adapters#react-router-v7): `react-router@^7` + +- [React Router v8](/docs/adapters#react-router-v8): `react-router@^8` + - [TanStack Router](/docs/adapters#tanstack-router): `@tanstack/react-router@^1` diff --git a/packages/docs/content/docs/options.mdx b/packages/docs/content/docs/options.mdx index 01dcaa42e..17603ae79 100644 --- a/packages/docs/content/docs/options.mdx +++ b/packages/docs/content/docs/options.mdx @@ -404,6 +404,7 @@ adapter prop: ``` + Setting `history: 'push'{:ts}` globally will add a new entry to the navigation history for **every** search param update across your whole app, which is likely @@ -413,6 +414,7 @@ Prefer opting into `history: 'push'{:ts}` on a per-hook or per-call basis, only where the search params contribute to a navigation-like experience (eg: tabs, modals). + ### Processing `URLSearchParams` diff --git a/packages/docs/mdx-components.tsx b/packages/docs/mdx-components.tsx index 019371ae9..6144f5e59 100644 --- a/packages/docs/mdx-components.tsx +++ b/packages/docs/mdx-components.tsx @@ -1,5 +1,6 @@ import { HumanContent, LLMContent } from '@/src/components/audience' import { FeatureSupportMatrix } from '@/src/components/feature-support-matrix' +import { SinceVersion } from '@/src/components/since-version' import { Callout } from 'fumadocs-ui/components/callout' import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock' import { Tab, Tabs } from 'fumadocs-ui/components/tabs' @@ -23,6 +24,7 @@ const components = { FeatureSupportMatrix, HumanContent, LLMContent, + SinceVersion, Suspense, Tab, Tabs, diff --git a/packages/docs/src/app/styles/tweaks.css b/packages/docs/src/app/styles/tweaks.css index dc5670ede..11c65a1f7 100644 --- a/packages/docs/src/app/styles/tweaks.css +++ b/packages/docs/src/app/styles/tweaks.css @@ -13,6 +13,24 @@ margin-left: -5.5px; margin-right: 5.5px; } + + /* + Inline "visible from" cue for unreleased content shown on preview/local + (see ). Rendered as a ::after pseudo-element + so it never fragments the host markdown list the way a block wrapper would. + */ + .nuqs-since-version::after { + content: ' (visible from nuqs@' attr(data-since) ')'; + @apply text-zinc-500 dark:text-zinc-400; + font-size: 0.85em; + } + + /* Collapse a list item whose only content is a hidden inline gate (production), + so it leaves no stray bullet. The gate's marker is targeted via :has() because + the emptied
  • retains whitespace/comment nodes and so is not :empty. */ + .prose li:has(> .nuqs-gated-empty) { + display: none; + } } /* Center-align copy button in code blocks with single line */ diff --git a/packages/docs/src/components/feature-support-matrix.tsx b/packages/docs/src/components/feature-support-matrix.tsx index da6c2a319..8178f09fd 100644 --- a/packages/docs/src/components/feature-support-matrix.tsx +++ b/packages/docs/src/components/feature-support-matrix.tsx @@ -1,3 +1,4 @@ +import { getPublishedVersion, isReleased } from '@/src/lib/published-version' import { cn } from '@/src/lib/utils' import { Callout } from 'fumadocs-ui/components/callout' import { CheckCircle, XCircle } from 'lucide-react' @@ -80,7 +81,7 @@ export function renderFeatureSupportMatrixText( return sentences.join(' ') } -export function FeatureSupportMatrix({ +export async function FeatureSupportMatrix({ introducedInVersion, deprecatedInVersion, highlightUnsupported = false, @@ -92,22 +93,17 @@ export function FeatureSupportMatrix({ includeAllFrameworksSupport: true }) const unsupportedLabel = getUnsupportedFrameworksText(notSupportedIn) + const released = isReleased(introducedInVersion, await getPublishedVersion()) return ( - +
    - Introduced in version {introducedInVersion} - {deprecatedInVersion ? ( - <> - , and deprecated in version {deprecatedInVersion} - - ) : ( - '.' - )} + {!hideFrameworks && (
    @@ -207,6 +203,47 @@ export function FeatureSupportMatrix({ ) } +function calloutTone( + released: boolean, + deprecatedInVersion?: string +): 'info' | 'warning' | 'success' { + if (!released) { + return 'info' + } + return deprecatedInVersion ? 'warning' : 'success' +} + +function VersionLine({ + introducedInVersion, + deprecatedInVersion, + released +}: Pick< + FeatureSupportMatrixProps, + 'introducedInVersion' | 'deprecatedInVersion' +> & { released: boolean }) { + if (!released) { + return ( + <> + Coming in version {introducedInVersion} — not yet + released. + + ) + } + if (deprecatedInVersion) { + return ( + <> + Introduced in version {introducedInVersion}, and + deprecated in version {deprecatedInVersion} + + ) + } + return ( + <> + Introduced in version {introducedInVersion}. + + ) +} + function parseSupportFrameworks(markdown: string): 'all' | Frameworks[] { const frameworksValue = markdown.match(/frameworks:\s*('all'|"all"|\[[\s\S]*?\])/) ?.[1] diff --git a/packages/docs/src/components/sidebar-footer.tsx b/packages/docs/src/components/sidebar-footer.tsx index ab4533c1a..f47f4eb5a 100644 --- a/packages/docs/src/components/sidebar-footer.tsx +++ b/packages/docs/src/components/sidebar-footer.tsx @@ -1,13 +1,7 @@ -'use client' +import { getPublishedVersion } from '@/src/lib/published-version' -import { useEffect, useState } from 'react' - -export function SidebarFooter() { - const [version, setVersion] = useState(null) - useEffect(() => { - getLatestVersion().then(setVersion).catch(console.error) - }, []) - if (!version) return null +export async function SidebarFooter() { + const version = await getPublishedVersion() return (