feat: warn on invalid HTML nesting at render time#42
Merged
Conversation
Certain parent/child pairs get silently normalized by the browser's HTML parser: <p> inside <p>, block-level content inside <p>, nested anchors, interactive content inside <button>, nested forms. The live DOM ends up different from what SSR emitted, and hydration reports a confusing 'Expected <X> but not found in <Y>' mismatch far from the source of the actual bug. New shared helper (packages/dom/src/Render/validateNesting.ts) codifies the invalid pairs from the HTML spec's content-model rules. DOMRenderer and StringRenderer's appendChild call warnIfInvalidNesting on each pair — emits a targeted console.warn once per pair per process. Small runtime cost, catches the bug at its source. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Deploying effex with
|
| Latest commit: |
8b07d3d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://bea72b9c.effex.pages.dev |
| Branch Preview URL: | https://feat-nesting-validation-warn.effex.pages.dev |
Deploying effex-api with
|
| Latest commit: |
8b07d3d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://c3b5913b.effex-api.pages.dev |
| Branch Preview URL: | https://feat-nesting-validation-warn.effex-api.pages.dev |
This was referenced Jul 24, 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
Certain HTML parent/child pairs get silently normalized by the browser's parser — the live DOM ends up different from what SSR emitted, and Effex hydration reports a confusing `Expected but not found in Y` mismatch far from the actual bug's source. This PR catches the class at render time with a targeted `console.warn`.
Motivating case
Portfolio project had:
```ts
// Card.ts
$.p({ class: "text-sm..." }, children)
// HomePage.ts$.p($ .of("..."))) //
Card({...},
inside
```
SSR emits `
...
`. Browser parses that as `...
` (auto-closes outer `` at the inner one). Hydration walker expects `
` inside `
`, finds the sibling, reports "Expected
but not found in P" — nowhere near the actual bad callsite.
Approach
New shared helper `packages/dom/src/Render/validateNesting.ts`:
Wired into both renderer `appendChild` implementations:
Hydration renderer's `appendChild` is a no-op, so no wire-up needed there.
Covered nestings
` inside `
` plus all block-level tags inside `
`: `div`, `section`, `article`, `aside`, `ul`, `ol`, `form`, `table`, `h1`–`h6`, `hr`, `blockquote`, `address`, `figure`, `fieldset`, `pre`, `main`, `nav`, `header`, `footer`, `details`, `dialog`, `menu`
Not covered (yet — table structure is more complex): `` outside `//`, `` outside ``, etc. Can add later if it comes up.
Cost
The check is a lowercase string compare + a Set lookup per element append. Negligible even on large trees. And it only runs where `appendChild` runs — the check itself doesn't matter in SSG output shipped to production.
Test plan
`), dedup (once-per-pair), and case normalization.
🤖 Generated with Claude Code