Update the realtime compiler to serve the sitemap and RSS feed#2497
Update the realtime compiler to serve the sitemap and RSS feed#2497emmadesilva wants to merge 6 commits into
Conversation
85607a1 to
b8a26f7
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2497 +/- ##
===========================================
Coverage 100.00% 100.00%
Complexity 1720 1720
===========================================
Files 175 175
Lines 4308 4308
===========================================
Hits 4308 4308 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
b8a26f7 to
9b178e1
Compare
There was a problem hiding this comment.
Pull request overview
This PR updates the realtime compiler’s request routing so that GET /sitemap.xml and GET /feed.xml (or the configured RSS filename) are generated dynamically on each request, similar to how docs/search.json is served, avoiding stale/404 behavior when previewing locally.
Changes:
- Register sitemap/RSS as virtual routes after
overrideSiteUrl()runs, so they work in local dev without requiring a production site URL. - Update proxy-bypass logic to ensure sitemap/RSS requests are not treated as static assets.
- Add unit and integration tests validating dynamic sitemap and RSS responses and content types.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/realtime-compiler/src/Routing/Router.php | Registers sitemap/RSS virtual routes after site URL override; prevents proxying sitemap/RSS paths. |
| packages/realtime-compiler/src/Http/VirtualRouteController.php | Adds virtual route handlers that generate sitemap/RSS XML responses. |
| packages/realtime-compiler/src/RealtimeCompilerServiceProvider.php | Notes sitemap/RSS are registered dynamically in Router after URL override. |
| packages/realtime-compiler/tests/RealtimeCompilerTest.php | Adds unit-level coverage for sitemap and RSS virtual route responses. |
| packages/realtime-compiler/tests/Integration/IntegrationTest.php | Adds integration coverage ensuring sitemap/RSS are served with expected headers/content. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
I feel like this gate is unnecessary for the local development
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
f0b322c to
abcedef
Compare
Summary
The realtime compiler already serves
docs/search.jsonon the fly (it's regenerated on every request instead of being read from a stale build on disk), butsitemap.xmland the RSS feed were never wired up the same way — requests for them fell through to static-asset proxying and either 404'd or served a stale cached copy from a previoushyde build. This PR closes that gap.GET /sitemap.xmlandGET /{rss.filename}(defaultfeed.xml) are now generated fresh on every request, using the sameSitemapGenerator/RssFeedGeneratorclasses that backhyde build.hyde.urlto the local server address for normal requests, so there's no need to set a real domain just to preview your sitemap/feed locally.Architecture decisions
Why a virtual route instead of a real Hyde route
docs/search.jsonworks today because it's backed by a real HydeRoute—DocumentationSearchIndexis anInMemoryPagewith acompile()method, registered into the page/route collection. The realtime compiler just special-cases it inRouter::shouldProxy()so it isn't treated as a static asset, and lets the normalPageRouterresolve and compile it per request.Sitemap/RSS have no such route — they're produced only by
GenerateSitemap/GenerateRssFeedPostBuildTasks thatfile_put_contents()straight to disk during a fullhyde build. Two ways to close the gap were considered:DocumentationSearchIndex), removing thePostBuildTasks in favor of the normal per-page compile loop./ping,/dashboard,/_hyde/live-edit), calling the generator classes directly, and leavinghyde builduntouched.Went with option 2. Option 1 is the more literal parallel to how search.json works, but it changes production build output:
SitemapGeneratoriterates all routes, so sitemap.xml would end up listing itself (and feed.xml) as an entry, and the existingPostBuildTasks would need to be deleted to avoid generating the files twice. That's a real behavior change tohyde buildfor every user, not just realtime-compiler users, for a dev-server-only convenience. The virtual-route approach gets the same on-the-fly behavior in the dev server with zero risk tohyde build.Dropping the site-URL requirement for local dev
Features::hasSitemap()/hasRss()both requireHyde::hasSiteUrl()— a real, production-lookinghyde.url— since that's whathyde buildneeds to emit correct absolute<loc>/<link>URLs. Registering the virtual routes gated on that check directly in the service provider'sboot()meant the routes were only available if you'd already configured a production site URL, which defeats the point of a local preview: you'd get aRouteNotFoundExceptionfor/sitemap.xmlon a fresh project.But
Router::overrideSiteUrl()already always replaceshyde.urlwith the local server address (e.g.http://localhost:8080) for every request, specifically so previews don't reference production — unlesshyde.server.save_previewis enabled, in which case the override is deliberately skipped so a local URL doesn't get baked into a persisted preview build.So instead of checking the feature flags at
ServiceProvider::boot()(before the override runs), the sitemap/RSS routes are now registered by a newRouter::registerDynamicVirtualRoutes(), called afteroverrideSiteUrl(). This means:hasSiteUrl()is always satisfied by the local override, so sitemap/RSS "just work" with no config needed — matching how any other compiled page behaves locally.save_previewmode, the override is skipped, so the original site-URL requirement still applies, preserving the original safety rationale (nolocalhostURLs baked into a persisted, production-like build).Verified against a live
hyde serveinstance:curl localhost:8080/sitemap.xmlreturns200with valid XML using local URLs, with noSITE_URLconfigured anywhere.Known limitation
Router::shouldProxy()decides whether to proxy a request before the Laravel app is booted (a deliberate perf optimization — booting costs ~80ms and most requests are static assets that shouldn't pay for it). That meansconfig('hyde.rss.filename')isn't resolvable at that point.sitemap.xmlis hardcoded in the framework so that's an exact match either way, but the RSS check falls back to matching the default filename (feed.xml). If a project customizeshyde.rss.filename, requests to that custom filename will still be proxied as a static asset instead of generated on the fly. This is called out in a code comment at the check site. I believe that customizing the RSS feed filename is such a rare thing to do, that we don't need to slow down everything else to accommodate it. // Emma