Skip to content

fix(parser): preserve content around opener/closer with trailing whitespace#83

Merged
teezeit merged 1 commit intomainfrom
bugfix-whitespace-formatting
May 1, 2026
Merged

fix(parser): preserve content around opener/closer with trailing whitespace#83
teezeit merged 1 commit intomainfrom
bugfix-whitespace-formatting

Conversation

@teezeit
Copy link
Copy Markdown
Owner

@teezeit teezeit commented May 1, 2026

Summary

Fixes 4 bugs in remark-containers.ts (2 targeted + 2 incidental) and recovers content in 2 docs fixtures that was previously silently dropped.

Targeted bugs:

  • ::: card (trailing spaces on opener) — remark emits a hard `break` node, splitting the opener paragraph into multi-child. `collectContainer` only handled `children.length === 1`, so post-opener content was silently dropped → empty container.
  • ::: (trailing space on closer) — CASE 1 recognized the closer line via `.trim()` but silently discarded any text after the closer.

Cascade — incidentally fixed:

  • `docs/page-layouts/sidebar-layout` and `sidebar-with-sections` had `.expected-fail.invariants.ts` for the same root cause (multi-child opener paragraph). Both invariants flipped to live.

Content recovery (no prior expected-fail invariants):

  • `docs/tabs/basic-tabs` — `Free — basic plan` / `Pro — $12/mo` lines no longer dropped.
  • `docs/navigation/sidebar-nav` — `App` brand line no longer dropped.

What changed

packages/core/src/parser/remark-containers.ts

  1. `collectContainer` now returns `{ node, trailing?, nextIndex }`; `processNodes` and the two recursive callsites push trailing siblings after the container.
  2. CASE 3 has a new `else if` branch for multi-child openers — extracts the post-opener children (skipping a leading hard `break` if present) into a synthetic content paragraph.

Tests

  • New `tests/parser/remark-containers-whitespace.test.ts` — 3 isolated parse tests pinning the opener/closer whitespace contract (uses the parser-contract test helpers from PR refactor(parser): TransformContext with explicit lookahead #82).
  • 4 fixture invariants flipped from `.expected-fail` to live; `KNOWN_FAILURES` in `review-gate.test.ts` trimmed.
  • 6 `REVIEW_LOG.md` rows ⏳→✅ (HTML output verified by diff inspection — see note below).

Known caveat

The React/Tailwind snapshots for `basic-tabs` and `sidebar-nav` reveal a pre-existing latent bug: `transformParagraph` builds inline `strong`/`em` as HTML string literals (lines 776-786 of transformer.ts), which render correctly in HTML but escape to `<strong>` in React/JSX. This pattern already exists in 10+ other fixture snapshots — not introduced here. Separate PR will land a proper inline-children fix.

Test plan

  • `pnpm typecheck` — clean
  • `pnpm test` — 1,187 passed (1,184 baseline + 3 new isolated remark-containers tests)
  • `pnpm review:refresh` — 6 fixtures flipped to ⏳ then verified ✅ (HTML output is correct; React/Tailwind escape is a separate latent bug)
  • All 4 expected-fail → live invariants pass; no other expected-fail tests changed

🤖 Generated with Claude Code

…espace

Two related bugs in remark-containers, plus four bugs fixed in cascade.

Targets:
  - `::: card   ` (trailing spaces on opener) → remark emits a hard `break`
    node, splitting the opener paragraph into [text, break, text, …].
    `collectContainer` only handled `children.length === 1`, so post-opener
    content was silently dropped, producing an empty container.
  - `::: ` (trailing space on closer) → CASE 1 recognized the closer line
    via `.trim()` but discarded any text after the closer, instead of
    emitting it as a sibling node.

Fixes:
  1. CASE 1 now returns trailing siblings via a new `trailing` field on
     `collectContainer`'s return shape; `processNodes` and the recursive
     callsites push them after the container.
  2. CASE 3 has a new branch for multi-child openers (children.length > 1
     with a leading text node) that extracts the post-opener children
     into a synthetic paragraph, mirroring the existing single-text-child
     branch.

Cascade — incidentally fixed:
  - `docs/page-layouts/sidebar-layout` and `sidebar-with-sections` had
    `.expected-fail.invariants.ts` files for the same root cause: the
    `::: sidebar` opener with no blank line broke parsing because remark
    inlined content into the multi-child opener paragraph.
  - `docs/tabs/basic-tabs` and `docs/navigation/sidebar-nav` snapshots
    recover previously-dropped `**Free**`/`**App**` brand and pricing
    content. The HTML output is now correct; their React/Tailwind
    snapshots show the pre-existing latent bug where `transformParagraph`
    emits inline `<strong>` as an HTML *string literal*, which renders
    fine in HTML but escapes in React/JSX. That escape pattern already
    exists in 10+ other fixture snapshots — separate PR will land a
    proper inline-children fix.

Tests:
  - 1,184 baseline → 1,187 with new isolated parse tests for remark-
    containers' opener/closer whitespace contract.
  - All 4 affected fixtures' invariants flipped from `.expected-fail` to
    live, KNOWN_FAILURES list trimmed accordingly.
  - REVIEW_LOG.md ⏳→✅ for the 6 affected fixtures (visually verified
    via diff inspection of HTML output).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@teezeit teezeit merged commit 10a991f into main May 1, 2026
8 checks passed
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.

1 participant