Add explicit nested layout chains - #311
Merged
Merged
Conversation
Coverage Report for CI Build 34160197572Coverage increased (+0.9%) to 95.309%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats💛 - Coveralls |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
PageData’s renderInnerPage()/renderFullPage() JSDoc still incorrectly constrains the pages array to homogeneous generics despite the new heterogeneous chain behavior.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR introduces explicit nested layout chains via a static parentLayout export so DOMStack can resolve and track full ancestor layout composition (vars/assets/deps) without relying on manual function composition.
Changes:
- Add
resolveLayoutChain()and integrate it into page init/render to merge vars outer-to-inner and renderroot(child(page()))while preserving intermediate render values. - Persist and use resolved layout chains in watch mode to correctly rebuild source and generated pages when ancestor layouts or shared helpers change.
- Update docs/tests/examples to demonstrate
parentLayout, migration guidance, and regressions for nesting, cycles, and watch invalidation.
File summaries
| File | Description |
|---|---|
| test-cases/nested-layouts/types.test.ts | Adds TypeScript-level assertions for heterogeneous/async/manual layout render typing. |
| test-cases/nested-layouts/index.test.js | Adds end-to-end coverage for nested layout rendering, asset ordering, and watch invalidation. |
| README.md | Documents the new parentLayout API, asset behavior, and manual-composition guidance. |
| lib/build-pages/resolve-layout-chain.test.js | Adds unit tests for chain resolution, validation, missing parents, and cycles. |
| lib/build-pages/resolve-layout-chain.js | Implements outer-to-inner layout chain resolution based on parentLayout. |
| lib/build-pages/page-data.js | Resolves and renders full layout chains; merges vars/assets across ancestors. |
| lib/build-pages/page-builders/page-writer.js | Updates pages typing to allow heterogeneous page/layout render result types. |
| lib/build-pages/index.js | Validates layout chains up-front and reports resolved chain names per page build. |
| index.js | Persists resolved layout chains for watch routing and unions affected rebuild roles per change. |
| examples/blog/src/layouts/year-index.layout.ts | Migrates example layout to parentLayout instead of manual root invocation. |
| examples/blog/src/layouts/post.layout.ts | Migrates example layout to parentLayout instead of manual root invocation. |
| examples/blog/src/layouts/post.layout.css | Removes manual parent CSS import now that ancestor assets are automatic. |
| docs/v12-migration.md | Adds migration guidance for adopting parentLayout and avoiding double-render pitfalls. |
Review details
Suppressed comments (1)
lib/build-pages/page-data.js:335
- Same issue as
renderInnerPage(): thepagesarray passed torenderFullPage()is a site-wide collection and can contain heterogeneousPageDatarender types, so this should not be constrained toPageData<T, U, V>[].
/**
* Render the full contents of a page with its layout
* @param {object} params The params required to render the page
* @param {PageData<T, U, V>[]} params.pages An array of initialized PageDatas.
*/
- Files reviewed: 13/13 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
bcomnes
commented
Sep 7, 2026
bcomnes
commented
Sep 7, 2026
bcomnes
commented
Sep 7, 2026
bcomnes
commented
Sep 7, 2026
bcomnes
commented
Sep 7, 2026
bcomnes
commented
Sep 7, 2026
bcomnes
commented
Sep 7, 2026
bcomnes
commented
Sep 7, 2026
bcomnes
marked this pull request as ready for review
September 7, 2026 20:06
bcomnes
commented
Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add explicit nested layouts through a static named
parentLayoutexport. Pages continue to choose their innermost layout throughvars.layout.DOMStack resolves the chain, merges layout defaults outermost-to-innermost, and renders
root(article(page())). Ancestor styles and client entry points are included automatically between global and page assets. Async and non-string intermediate layout results are preserved until final serialization. Missing parents, invalid exports, and cycles produce explicit build errors.Watch routing now uses the actual resolved layout chain reported by successful builds for source and generated pages. It follows ancestor edits, ordinary helper imports, reparenting, and layout asset membership without re-evaluating a partial approximation of page vars. Existing single layouts and manual function composition remain supported and tested for source-backed and generated pages. Static dependency routing unions every affected role, so a directly selected layout can also be a manually imported parent, and helpers shared by layouts, pages, templates, and factories rebuild all of their consumers. Browser entry points shared with server-side modules also rebuild their server consumers while esbuild rebundles the client. Watch retries the complete page phase after a failure, including initial layout render, vars, or declaration failures before any successful routing state exists. After recovery, targeted rebuilds resume. The layout registry accepts heterogeneous intermediate render types; individual render functions retain their precise input and output contracts.
The blog example now uses formal nesting, and the README and v12 migration guide explain both the new API and migration pitfalls.
Stack
This is the layout-composition prerequisite for #294. Global-data subscriptions remain in that follow-up PR, which will use these resolved chains. The unrelated watch-maintenance stack remains based on master.
Validation
Fixes #310. Fixes #290.