Skip to content

feat(dx): dev-mode warning for unbalanced template tags (#1769) - #1770

Merged
glennmichael123 merged 1 commit into
mainfrom
feat/1769-tag-balance-dev-warning
Jul 6, 2026
Merged

feat(dx): dev-mode warning for unbalanced template tags (#1769)#1770
glennmichael123 merged 1 commit into
mainfrom
feat/1769-tag-balance-dev-warning

Conversation

@glennmichael123

Copy link
Copy Markdown
Member

Closes #1769.

The problem

A single dropped closing tag (a missing </div>) is silently auto-closed by the HTML parser at EOF, re-parenting every following sibling inside the unclosed element. Those siblings inherit its x-show/display:none and render blank — with zero diagnostics at any layer: the server 200s, hydration succeeds, no console output. It's only visible by eye (or by walking getComputedStyle up the ancestor chain).

The fix

A coarse, void- and optional-close-aware open/close count per tag name, run at template-parse time and warning on any mismatch:

[stx] SettingsTab.stx: <div> 47 opened, 46 closed — an unclosed <div> (first opened at line 771) may swallow the content that follows it.
  • template-tag-balance.ts — a small module: pure findUnbalancedTags() (fully unit-tested) + warnUnbalancedTags(). It's deliberately a count, not a parse — the goal is a cheap, high-signal heads-up.
  • Where it runs: processDirectives on the raw template of views/layouts, and the include pipeline on each @included partial's raw content (the issue's exact case — a tab component). Both check the RAW source before any processing auto-closes tags.
  • When it runs: on by default in development; opt-in via debug elsewhere (options.debug || (!isProduction() && !isTest())). Off in the production build and under the test runner, so it never adds noise to CI or the shipped bundle. Deduped per file (warns once; re-arms on edit).

False-positive-free by design

It ignores, and there are tests for each: void elements (<br>, <img>, …), optional-close elements (<li>, <td>, <p>, …), self-closing components (<Icon />), and any </> inside <script>/<style>/comments/{{ }} interpolations/quoted attribute values. Component tags (<StxLink>…</StxLink>) are checked case-preserved, so an unbalanced component is caught too.

Verification

  • 14 new tests (test/template-tag-balance.test.ts): the pure counter across all the edge cases above, warnUnbalancedTags message/dedupe, and a processDirectives integration test confirming the hook fires in dev and stays quiet otherwise.
  • Full suite green: 7810 pass, 0 fail.
  • Manually confirmed: fires in NODE_ENV=development with no debug flag; silent in NODE_ENV=production.

Notes for review

  • The default-on-in-dev gate is the deliberate call (the request was to catch this during development, and debug defaults to false). Easy to flip to debug-only if you'd rather it be opt-in.
  • It reports every imbalanced tag name (unclosed and stray-close), keyed by the first opening line.

A single dropped closing tag (e.g. a missing </div>) is silently auto-closed
at EOF, re-parenting every following sibling inside the unclosed element —
which then inherits its x-show/display:none and renders blank, with zero
diagnostics at any layer (server 200s, hydration succeeds, no console output).

Add a coarse, void- and optional-close-aware open/close COUNT per tag name,
run on the RAW template of views/layouts (in processDirectives) and @include'd
partials (in the include pipeline), that warns on a mismatch, e.g.:

  [stx] SettingsTab.stx: <div> 47 opened, 46 closed — an unclosed <div>
  (first opened at line 771) may swallow the content that follows it.

- New template-tag-balance module: pure findUnbalancedTags() + warnUnbalancedTags().
  Ignores void elements, optional-close elements (li/td/p/...), self-closing
  components, and tags inside scripts/styles/comments/interpolations/attributes,
  so it stays false-positive-free.
- On by default in development; opt-in via 'debug' elsewhere; off in the prod
  build and under the test runner (so it never adds noise to CI or the bundle).
- Deduped per file so a broken file warns once, re-arming on edit.

14 new tests; full suite green (7810 pass, 0 fail).
@netlify

netlify Bot commented Jul 6, 2026

Copy link
Copy Markdown

Deploy Preview for stacks-stx failed. Why did it fail? →

Name Link
🔨 Latest commit 46e7d1e
🔍 Latest deploy log https://app.netlify.com/projects/stacks-stx/deploys/6a4bc800e55c6300096f9f14

@glennmichael123
glennmichael123 merged commit 1ed4e1b into main Jul 6, 2026
2 of 7 checks passed
@glennmichael123
glennmichael123 deleted the feat/1769-tag-balance-dev-warning branch July 6, 2026 15:24
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.

DX: unbalanced tags in a component template fail silently — subtree swallowed with zero diagnostics

1 participant