Skip to content

[3.x] Support versioned documentation pages#2516

Merged
emmadesilva merged 52 commits into
masterfrom
v3/support-versioned-documentation-pages
Jul 11, 2026
Merged

[3.x] Support versioned documentation pages#2516
emmadesilva merged 52 commits into
masterfrom
v3/support-versioned-documentation-pages

Conversation

@emmadesilva

Copy link
Copy Markdown
Member

No description provided.

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (4d58f38) to head (e7b362b).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##              master     #2516    +/-   ##
============================================
  Coverage     100.00%   100.00%            
- Complexity      1608      1676    +68     
============================================
  Files            168       170     +2     
  Lines           4039      4187   +148     
============================================
+ Hits            4039      4187   +148     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Base automatically changed from v3-dev to master July 11, 2026 12:54
emmadesilva and others added 10 commits July 11, 2026 15:03
This doubles as a handoff document tracking implementation progress
for the native versioned documentation feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the DocumentationVersion value object, the DocumentationVersions
registry reading the new docs.versions and docs.default_version config
options, and the HasDocumentationVersion interface. Versioning is
disabled by default when the versions list is empty.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Documentation pages stored in registered version subdirectories now
resolve their version, keep the version prefix when flattening output
paths, and the home route helpers accept a version, defaulting to the
default version when versioning is enabled.

The version registry now also tolerates being called from configuration
files while the configuration itself is being loaded, treating
versioning as disabled at that point.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each documentation version now gets its own sidebar, registered as
navigation.sidebar.{version} container singletons, with the plain
navigation.sidebar binding holding the default version's sidebar.
DocumentationSidebar::get() resolves the sidebar for the version of
the page currently being rendered.

Sidebar and navigation configuration entries (order, labels, exclude)
now also match version-agnostic identifiers and route keys, so a
single configuration entry applies to the page in every version,
while still allowing version-specific overrides via full keys.

Automatic sidebar grouping and index page titles skip the version
directory segment, and the main navigation menu links to the default
version's documentation index page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When documentation versioning is enabled, each version now gets its own
search index and search page (docs/1.x/search.json, docs/1.x/search),
containing only the pages belonging to that version. The search
component resolves the index for the version of the rendered page,
and the build:search command builds all version shards.

Version-agnostic docs.exclude_from_search entries apply to the page
in every version.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a version switcher dropdown to the documentation sidebar, shown
when documentation versioning is enabled. It links to the equivalent
page in each version when it exists, falling back to that version's
index page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When documentation versioning is enabled, a redirect page is generated
at the documentation root (docs/index) pointing to the default
version's index page, so inbound links to the docs root always have a
destination and no duplicate HTML is built for it. Creating your own
page with the docs/index route key overrides the generated redirect.

The Redirect page class now accepts optional front matter, which the
generated redirect uses to hide itself from navigation menus.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds release notes, an upgrade guide section for adopting versioned
documentation, and marks the design document as implemented.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
@emmadesilva emmadesilva force-pushed the v3/support-versioned-documentation-pages branch from 8ec3c8f to 2cb6ae2 Compare July 11, 2026 13:18
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 13:18 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 13:40 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 13:59 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 14:55 — with GitHub Actions Inactive
The real-time compiler dev server previously misclassified paths with dotted segments (like `/docs/1.x`) as static assets by blindly parsing the last segment as a file extension (e.g., `x`). This caused versioned documentation routes to bypass the PageRouter and incorrectly return a 404.

This commit updates `Router::shouldProxy()` to stop guessing based on extensions. Instead, it delegates to `AssetFileLocator::find()` to check if the static asset actually exists on disk. If it doesn't, the request properly falls through to the PageRouter.

Key implementation details:
* Config Independence: By checking the disk directly, custom extensions added via the `hyde.media_extensions` config are automatically supported. This avoids the trap of using a hardcoded extension allowlist before the application has booted.
* Performance Optimization: Added memoization (`resolveAssetPath()`) to cache the `AssetFileLocator::find()` result per request. This eliminates a pre-existing duplicate I/O check where both `shouldProxy()` and `proxyStatic()` were querying the disk, ensuring dev server response times remain negligible.
* Test Updates: Updated `RealtimeCompilerTest` to reflect the new fallback behavior. Missing extension-like paths outside of `/media/` now accurately hit the `RouteNotFoundException` via the page router rather than returning a raw static 404.
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 15:42 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 16:22 — with GitHub Actions Inactive
@emmadesilva emmadesilva marked this pull request as ready for review July 11, 2026 16:27
emmadesilva and others added 4 commits July 11, 2026 19:05
The previous commit made the router fall through to the page router for any
path with a dotted segment that isn't an existing file, so that versioned
documentation routes like `/docs/1.x` resolve. But that also redefined every
missing dotted resource as a missing page: a request for a nonexistent
stylesheet, source map, or manifest would throw a RouteNotFoundException and
render the pretty error page, instead of returning a clean 404 as it did
before. Browsers request such files on their own, so this would also create
exception noise in the dev server.

We now still route dotted paths through the page router, but convert a route
miss on an asset-like path back into the static 404 response. The shared
`looksLikeAsset()` helper keeps the definition in one place, so that missing
`.html` paths still throw, just like they did before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The generated `docs/index.html` redirect pointed to the default version's
index page without checking that the page exists, so a site where the default
version has no index page would get a redirect to a dead link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The sidebar configuration options match pages by identifier or route key, in
both their version-specific and version-agnostic forms, and the upgrade guide
documents the search exclusions as working the same way. But the search index
only compared identifiers, so a documented entry like `docs/2.x/readme` was
silently ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The switcher took its current version from the sidebar, which falls back to the
default version's sidebar when the rendered page has no version. Documentation
pages stored outside the version directories were thus presented as belonging
to the default version, and switching versions would take you to a version home
page instead of an equivalent page.

The switcher now takes its current version from the rendered page, and is
hidden when that page does not belong to a version.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 17:07 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 21:31 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 21:31 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 21:38 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 21:44 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 22:03 — with GitHub Actions Inactive
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 22:10 — with GitHub Actions Inactive
@emmadesilva emmadesilva force-pushed the v3/support-versioned-documentation-pages branch from b6bdd59 to 63ef13a Compare July 11, 2026 22:18
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 22:18 — with GitHub Actions Inactive
emmadesilva and others added 5 commits July 12, 2026 00:32
The version switcher passes whatever Render::getPage() returns into
getEquivalentRoute(), so restricting it to DocumentationPage and
DocumentationSearchPage raised a TypeError when a versioned page was
overridden by a custom page, which the feature explicitly supports.

Resolving the version from the route key instead of matching concrete
page classes also lets the sidebar and version switcher support custom
overrides and future version-aware page types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Centralizing the configuration keys meant hyde.navigation.exclude also
matched documentation identifiers and version-agnostic keys, which
broadened its public contract beyond the route keys it accepted before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The memoized path used false, null, and string to mean three different
things, and looksLikeAsset() only checked the extension, so both needed
a comment to decode them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The upgrade guide already explains the versions configuration in full,
and the removed comments restated their test or method names.

Also document the front matter parameter the Redirect constructor added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 22:34 — with GitHub Actions Inactive
The switcher declared role="listbox"/role="option" but its options
were plain links with no listbox keyboard behavior implemented. Treat
it as a disclosure containing a plain list of links instead: drop the
listbox/option roles and aria-haspopup/aria-selected, mark the active
version with aria-current="page", and connect the button to the list
via aria-controls.

Also close the dropdown on Escape and return focus to the trigger
button, and cap the list height with a scrollbar so a long list of
versions stays usable without overflowing the viewport.
@emmadesilva emmadesilva temporarily deployed to pr-documentation-2516 July 11, 2026 22:55 — with GitHub Actions Inactive
@emmadesilva emmadesilva linked an issue Jul 11, 2026 that may be closed by this pull request
@emmadesilva emmadesilva merged commit 7d5b233 into master Jul 11, 2026
21 checks passed
@emmadesilva emmadesilva deleted the v3/support-versioned-documentation-pages branch July 11, 2026 23:37
@emmadesilva emmadesilva linked an issue Jul 12, 2026 that may be closed by this pull request
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.

[3.x] Support versioned documentation pages Version selector

1 participant