Uh oh!
There was an error while loading. Please reload this page.
[3.x] First-class non-HTML pages and generated discovery files (epic) - #2524
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@## master #2524 +/- ##
===========================================
Coverage 100.00% 100.00% - Complexity 1796 1807 +11
===========================================
Files 185 183 -2 Lines 4444 4474 +30 ===========================================
+ Hits 4444 4474 +30 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
edee07f to
62feb3cCompareb406e43 to
2146b89Compare9486d97 to
1dafca8Compare# Conflicts: # HYDEPHP_V3_PLANNING.md
There was a problem hiding this comment.
Pull request overview
Adds first-class non-HTML page output across routing, builds, sitemap policy, and realtime serving.
Changes:
- Adds configurable output extensions and renames source-extension APIs.
- Converts sitemap and RSS generation into registered pages.
- Updates navigation, sitemap, realtime compiler, tests, and documentation.
Reviewed changes
Copilot reviewed 66 out of 66 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
UPGRADE.md | Adds v3 migration guidance. |
packages/testing/src/Common/BaseHydePageUnitTest.php | Extends page test contract. |
packages/realtime-compiler/tests/RealtimeCompilerTest.php | Tests non-HTML route serving. |
packages/realtime-compiler/tests/Integration/IntegrationTest.php | Tests generated XML routes. |
packages/realtime-compiler/src/Routing/Router.php | Prioritizes registered routes. |
packages/realtime-compiler/src/Routing/PageRouter.php | Adds RSS content type. |
packages/realtime-compiler/src/Http/DashboardController.php | Uses renamed source API. |
packages/framework/tests/Unit/RouteKeyTest.php | Tests extension-aware route keys. |
packages/framework/tests/Unit/Pages/MarkdownPostUnitTest.php | Updates page API tests. |
packages/framework/tests/Unit/Pages/MarkdownPageUnitTest.php | Updates page API tests. |
packages/framework/tests/Unit/Pages/InMemoryPageUnitTest.php | Updates in-memory page tests. |
packages/framework/tests/Unit/Pages/InMemoryPageTest.php | Tests output inference. |
packages/framework/tests/Unit/Pages/HtmlPageUnitTest.php | Updates HTML page tests. |
packages/framework/tests/Unit/Pages/DocumentationPageUnitTest.php | Updates documentation page tests. |
packages/framework/tests/Unit/Pages/BladePageUnitTest.php | Updates Blade page tests. |
packages/framework/tests/Unit/NumericalPageOrderingHelperUnitTest.php | Uses renamed source property. |
packages/framework/tests/Unit/NavigationDataFactoryUnitTest.php | Tests navigation exclusions. |
packages/framework/tests/Unit/ExtensionsUnitTest.php | Updates extension fixture. |
packages/framework/tests/Unit/BuildTaskServiceUnitTest.php | Removes generator task fixtures. |
packages/framework/tests/Feature/StaticSiteServiceTest.php | Verifies generated output files. |
packages/framework/tests/Feature/SitemapPageTest.php | Tests sitemap page lifecycle. |
packages/framework/tests/Feature/SitemapFeatureTest.php | Updates sitemap expectations. |
packages/framework/tests/Feature/Services/SitemapServiceTest.php | Tests sitemap inclusion policy. |
packages/framework/tests/Feature/Services/BuildTaskServiceTest.php | Updates build-task assertions. |
packages/framework/tests/Feature/RssFeedPageTest.php | Tests RSS page lifecycle. |
packages/framework/tests/Feature/RouteCollectionTest.php | Isolates generated routes. |
packages/framework/tests/Feature/RedirectTest.php | Tests sitemap exclusion. |
packages/framework/tests/Feature/PageCollectionTest.php | Isolates generated pages. |
packages/framework/tests/Feature/NonHtmlPageOutputTest.php | Tests non-HTML builds. |
packages/framework/tests/Feature/HydePageTest.php | Tests extension APIs. |
packages/framework/tests/Feature/HydeExtensionFeatureTest.php | Updates extension fixtures. |
packages/framework/tests/Feature/DocumentationSearchIndexTest.php | Verifies JSON output path. |
packages/framework/tests/Feature/DiscoveryServiceTest.php | Tests renamed discovery API. |
packages/framework/tests/Feature/Commands/RouteListCommandTest.php | Isolates sitemap route. |
packages/framework/tests/Feature/Commands/BuildSitemapCommandTest.php | Tests page-backed command. |
packages/framework/tests/Feature/Commands/BuildRssFeedCommandTest.php | Tests page-backed command. |
packages/framework/tests/Feature/AutomaticNavigationConfigurationsTest.php | Tests non-HTML navigation. |
packages/framework/src/Support/Models/RouteKey.php | Adds extension-aware keys. |
packages/framework/src/Support/Models/Redirect.php | Excludes redirects from sitemaps. |
packages/framework/src/Pages/InMemoryPage.php | Infers output extensions. |
packages/framework/src/Pages/HtmlPage.php | Renames source extension. |
packages/framework/src/Pages/Concerns/HydePage.php | Adds output and sitemap APIs. |
packages/framework/src/Pages/Concerns/BaseMarkdownPage.php | Renames source extension. |
packages/framework/src/Pages/BladePage.php | Renames source extension. |
packages/framework/src/Framework/Services/BuildTaskService.php | Removes XML generation tasks. |
packages/framework/src/Framework/Features/XmlGenerators/SitemapGenerator.php | Applies page sitemap policy. |
packages/framework/src/Framework/Features/Documentation/DocumentationSearchIndex.php | Uses inferred JSON output. |
packages/framework/src/Framework/Factories/NavigationDataFactory.php | Hides non-HTML outputs. |
packages/framework/src/Framework/Actions/PostBuildTasks/GenerateSitemap.php | Removes sitemap task. |
packages/framework/src/Framework/Actions/PostBuildTasks/GenerateRssFeed.php | Removes RSS task. |
packages/framework/src/Foundation/Kernel/FileCollection.php | Uses source-extension API. |
packages/framework/src/Foundation/HydeCoreExtension.php | Registers generated XML pages. |
packages/framework/src/Console/Commands/BuildSitemapCommand.php | Builds registered sitemap page. |
packages/framework/src/Console/Commands/BuildRssFeedCommand.php | Builds registered feed page. |
HYDEPHP_V3_PLANNING.md | Records v3 behavior changes. |
docs/getting-started/console-commands.md | Documents XML build commands. |
docs/digging-deeper/navigation.md | Documents explicit visibility. |
docs/digging-deeper/customization.md | Documents generator customization. |
docs/architecture-concepts/page-models.md | Updates source property examples. |
docs/advanced-features/in-memory-pages.md | Documents output inference. |
docs/advanced-features/hyde-pages.md | Documents output extensions. |
docs/advanced-features/build-tasks.md | Removes obsolete task guidance. |
docs/_data/partials/hyde-pages-api/in-memory-page-methods.md | Regenerates in-memory API docs. |
docs/_data/partials/hyde-pages-api/hyde-page-methods.md | Regenerates page API docs. |
CHANGELOG.md | Notes navigation behavior. |
AI_CODE_REVIEW.md | Removes trailing blank line. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 66 out of 66 changed files in this pull request and generated no new comments.
Suppressed comments (7)
packages/framework/src/Foundation/HydeCoreExtension.php:113
- Passing the configured RSS filename through normal
InMemoryPageinference does not preserve it verbatim. Forhyde.rss.filename = 'feed', the build writesfeed.html; forfeed.html, the registered route becomesfeedwhilebuild:rsslooks upfeed.htmland fails. Preserve the configured value as an exact output path, as the previous generator did.
}
packages/framework/src/Foundation/HydeCoreExtension.php:120
- This check runs in the core extension before user extensions discover their pages. A user extension registering the same route from a custom page class therefore cannot suppress the default when its source-path collection key differs; both pages remain in
Hyde::pages(), contrary to the documented full replacement behavior. Evaluate generated defaults in a second discovery phase after all normal extension pages.
if (Config::getBool('hyde.server.running', false)) {
packages/framework/src/Pages/Concerns/HydePage.php:225
- The route still comes from class-level declarations rather than the resolved instance output path. A page that overrides only
getOutputPath()can therefore writefeed.xmlwhile remaining registered under its identifier, so builds andhyde servedisagree about its URL. This also misses the linked refactor's acceptance criterion for output-path-derived route keys. Make output-path construction independent here and derive the stored route key from the resolved output path.
$routeKey = RouteKey::fromPage(static::class, $identifier);
packages/realtime-compiler/src/Routing/PageRouter.php:69
- The MIME switch still treats every newly supported extension outside this short list as HTML. For example, a registered
styles.css,script.js, orreport.csvpage is returned throughHtmlResponsewithtext/html, so it does not participate correctly inhyde serve. Reuse the repository's extension-based MIME resolver while retaining the RSS override.
packages/framework/tests/Feature/SitemapPageTest.php:24 - This test-only docblock restates the class name and the scenarios already expressed by the test methods; it does not provide static-analysis typing, public API documentation, or a non-obvious constraint. Remove the redundant narrative comment.
packages/framework/tests/Feature/RssFeedPageTest.php:25 - This test-only docblock restates the class name and the scenarios already expressed by the test methods; it does not provide static-analysis typing, public API documentation, or a non-obvious constraint. Remove the redundant narrative comment.
packages/framework/tests/Feature/NonHtmlPageOutputTest.php:21 - This docblock only narrates what the clearly named feature test class covers and adds no typing or non-obvious rationale. Remove the redundant comment.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 66 out of 66 changed files in this pull request and generated no new comments.
Suppressed comments (5)
packages/framework/src/Foundation/HydeCoreExtension.php:112
- The RSS default has the same unconditional registration problem as the sitemap: it can replace a page added in a booting callback, and it can win the route over a source-discovered page with the configured feed route. Generated defaults should be registered only after user pages and only when no page already claims this route.
$collection->addPage(new InMemoryPage(
RssFeedGenerator::getFilename(),
contents: fn (): string => app(RssFeedGenerator::class)->generate()->getXml(),
));
packages/framework/src/Foundation/HydeCoreExtension.php:112
- Passing the configured filename to ordinary
InMemoryPageno longer preserves extensionless RSS filenames. Forhyde.rss.filename = 'feed',InMemoryPage::outputPath()producesfeed.html, whereas the previous task wrote the configured filename verbatim and the generator still emits a self-link to/feed. Use an exact-output-path page API for this generated file so every configured filename, including one without an extension, remains literal.
$collection->addPage(new InMemoryPage(
RssFeedGenerator::getFilename(),
contents: fn (): string => app(RssFeedGenerator::class)->generate()->getXml(),
));
packages/framework/src/Support/Models/RouteKey.php:79
fromPage()still derives the route from static class settings rather than the page's resolved output path. A page that overrides onlygetOutputPath()can therefore writecustom.jsonwhile remaining registered under its original route (for examplefoo), so the generated file cannot be served through its actual path. Derive route keys from the resolved output path and strip only a trailing.htmlinstead of duplicating output-path rules here.
$identifier = self::stripPrefixIfNeeded($pageClass, $identifier);
$key = unslash("{$pageClass::baseRouteKey()}/$identifier");
$extension = $pageClass::outputExtension();
if (is_a($pageClass, InMemoryPage::class, true) && pathinfo($identifier, PATHINFO_EXTENSION) !== '') {
if (str_ends_with($key, '.html')) {
$key = substr($key, 0, -strlen('.html'));
}
return new self($key);
}
if ($extension !== '.html' && ! str_ends_with($key, $extension)) {
$key .= $extension;
}
return new self($key);
packages/framework/src/Foundation/HydeCoreExtension.php:104
- This default is added unconditionally after booting callbacks and source discovery. Because
PageCollectionis keyed by source path, it overwrites a booting-registeredInMemoryPage('sitemap.xml'); a custom page under another source key is also shadowed when routes are later collected. Register this as a fallback only after user page hooks have run, and skip it when any page already claimssitemap.xml.
This issue also appears on line 109 of the same file.
$collection->addPage(new InMemoryPage(
'sitemap.xml',
contents: fn (): string => app(SitemapGenerator::class)->generate()->getXml(),
));
packages/realtime-compiler/src/Routing/PageRouter.php:68
- Explicit
InMemoryPageidentifiers accept arbitrary extensions (the new tests include.md), but every extension outside this four-item list is returned astext/html. Registered.css,.js,.svg,.md, or other non-HTML pages will therefore be served with the wrong media type and may be rejected or misinterpreted by clients. Resolve the MIME type from the output extension, reservingtext/htmlfor HTML output and using a safe binary fallback for unknown types.
fc828bc to
136d5fcCompareUh oh!
There was an error while loading. Please reload this page.
This PR adds first-class support for non-HTML page output such as XML, JSON, and TXT files.
Previously, Hyde largely assumed pages compiled to
.html, while files such assitemap.xmlandfeed.xmlwere generated as post-build side effects and the realtime compiler needed special cases for routes likesearch.json. This PR makes output format part of the page model and allows registered non-HTML pages to participate normally in routing, builds,hyde serve, and user-land customization.As part of this, the sitemap and RSS feed become regular generated pages, non-HTML pages are excluded from navigation and sitemaps by default, and explicit front matter can override those defaults. This also fixes documentation search indexes incorrectly appearing in generated sitemaps.