ReviewOS

also looking at this

stacks/stx

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

#1770
Merged glennmichael123 wants to merge feat/1769-tag-balance-dev-warning into main
4 files +277 -0
9 days ago

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.

4 changed files on the files tab, with 0 review threads.

Sign in to comment on this pull request.