Skip to content

finding(tooling): js-comment-mask still opens a phantom regex on a self-closing JSX tag and on a slash in JSX text #7882

Description

@claude

Measured while implementing #6891, on cf7f2af6e. Filed unassigned, not fixed there: #6891 closes the LT SLASH half with a one-token rule; this half provably cannot be closed the same way, so it is its own decision.

The defect

scanSource opens a REGEX when the character before a / is not a value (IDENT_CHAR, ), ]). } and GT are not values, so both of these open a phantom regex that runs to end of line:

  • a self-closing JSX tag whose / follows a brace — Foo bar={x} SLASH GT, Foo {...p} SLASH GT
  • a / in JSX text{a}/{b}, or code OS_PAGE_REACT=off /code SLASH code x /code

A self-closing tag whose / follows a value (br SLASH GT, Foo SLASH GT, Foo x="1" SLASH GT) was never affected — the discriminator is the preceding byte, not the tag shape.

Measured

Over all 4,322 tracked source files, with #6891's fix applied and both flag arrays diffed byte for byte against the pre-fix scanner: 145 bytes across 28 files are still flagged literal by this shape. They were invisible before only because the closing tag's larger phantom started earlier on the same line and covered them.

It reaches the FABRICATES direction too — the phantom swallows a comment opener that follows it on the same line, so genuinely commented-out text comes back to a caller as live code:

const c = Foo bar={x} SLASH GT ; // dead = 1;

maskComments leaves dead = 1; standing. That is the direction this module's header calls worse than no verifier at all.

Why #6891 did not take it

There is no one-token rule for it, and the two obvious ones are both wrong:

  1. / followed by GT is a tag closer. That is exactly how a regex matching a GT is spelled — str.replace(/GT/g, '>') — and this tree writes that shape.
  2. } and GT are values. Both are genuinely ambiguous in JavaScript: function f(){} /re/ is a regex, and a GT /re/.test(b) parses.

Separating them needs to know whether the scanner is inside a JSX element, which is a parser. js-comment-mask.mjs is deliberately a cheap language-level masker, and turning it into a parser is a different card.

Already pinned, so this cannot be closed silently

scripts/__tests__/js-comment-mask-jsx-6891.test.ts carries a KNOWN LIMITS block asserting today's behaviour for all four shapes. Whoever closes this will see those pins fail and must retire them deliberately — the same two-way discipline that surfaced #6891 itself.

Shape of a fix, for triage rather than as a decision

  1. Leave it. The residue is 145 bytes, it is pinned, and no gate's verdict moves today. Cheapest, and honest as long as the pins stay.
  2. Port objectstack's corpus sweep (scripts/check-comment-mask-corpus.mjs, named in this module's header as NOT ported here) and decide against a measurement over an independent parser rather than against cases. This is the route finding(tooling): js-comment-mask reads a JSX closing tag as a regex literal — 7 measured call sites become undelimitable #6891's own triage comment named as the only one that answers "which direction does it fail in".
  3. A JSX-aware mode, opt-in per caller. Largest, and it changes what this module is.

Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repofindingpriority:p3tooling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions