Skip to content

finding(plugin-markdown): extractToc's emphasis rules eat intraword underscores, so a SCREAMING_SNAKE heading gets an id no anchor carries #7667

Description

@os-sam

Measured while implementing objectui#7658 (the inline-code-span anchor defect), by a corpus sweep run to check whether that fix left any parity gap behind. Not fixed there — different defect class, and the #7658 brief said to measure and file adjacent gaps rather than widen into a general parity project. Unassigned, no labels; routing and grading are the triage seat's.

Method

Truth source is the rendered heading, not a second derivation of the slug rules: every file is rendered through MarkdownImpl (the real remark/rehype chain this plugin ships) and the id attributes that come out are compared, in document order, with extractToc(src, { minDepth: 1, maxDepth: 6 }).

Corpus: content/docs/**/*.md(x) plus packages/*/README.md, YAML frontmatter stripped from both sides. 223 files, 2941 rendered headings — the population is lit, not a zero reading. With objectui#7658 fixed, 5 files still diverge; this is the fifth.

What was measured

stripInline() ends with two hand-rolled emphasis rules:

.replace(/(\*\*|__)(.*?)\1/g, "$2")   // bold
.replace(/(\*|_)(.*?)\1/g, "$2")      // italic

Neither knows CommonMark's flanking rule, under which an underscore inside a word never opens emphasis. The renderer follows that rule and keeps the underscores; extractToc deletes the pair it matched along with nothing else, and the two ids part company:

heading extractToc id id rehype-slug puts on the heading
### NON_GRID_ROW_CEILING nongridrow_ceiling non_grid_row_ceiling
## the snake_case name the-snake_case-name the-snake_case-name

The second row is the boundary, and it is why this went unnoticed: a single underscore has nothing to pair with, so ordinary snake_case words are untouched. It takes two or more underscores in one heading — the SCREAMING_SNAKE constant shape — for the italic rule to find a pair and swallow the text between them.

Live instance: packages/react/README.md:224### NON_GRID_ROW_CEILING. One heading in the measured corpus; the shape is common enough in API docs that it will recur.

The bold rule (__) has the same blind spot and no live instance was measured.

Also measured, not filed separately

extractToc matches only ATX headings (^#{1,6}), so a setext heading is invisible to it entirely — Setext Title over a --- rule renders as an h2 with id setext-title and produces no TOC entry at all. Zero live instances in the 223-file corpus, so this is a latent gap rather than a live one; recording it here because it belongs to the same surface and the same repair.

Both of these, plus objectui#7658 and the JSX-block gap filed alongside this card, come from the same root: extractToc re-implements markdown inline parsing by regex instead of walking the mdast tree the renderer already builds. Whether to keep patching the regexes or to switch to the mdast heading walk is the call this cluster is really asking for.

Refs: objectui#7658

Activity

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

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions