refactor(core): finish monolith split — all node types in nodes/#79
Merged
refactor(core): finish monolith split — all node types in nodes/#79
Conversation
The repo-wide rule `*.md text eol=lf` was forcing the CRLF fixture to LF on every checkout, surfacing a phantom modification on every branch. A more-specific rule restores byte preservation for that one file without affecting any other markdown. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Checkpoint commit on the way to PR 3 (full migration). Migrates 21
node types end-to-end through the registry/strategy contract:
- container (recursion validator: 11 subtypes, sidebar-main special case)
- layout: nav, nav-item, brand, grid, grid-item, row
- content: heading, paragraph, text, image, icon, link, list,
list-item, table, table-header, table-row, table-cell, blockquote,
code
Each migrated type:
- nodes/<type>/{html,react,tailwind,index}.ts — self-contained module
exporting NodeDefinition; legacy case arm + function deleted from
the four legacy renderer files.
- icon/_iconmap.ts — shared icon character map (60+ glyphs for HTML,
smaller subset for React/Tailwind, social-icons list).
Helpers exposed from legacy renderers (now `export`):
- html-renderer: applyRadioGroupName (was already), buildClasses,
escapeHtml, sourceLine, renderChildrenList (was already), renderNode
- react-renderer: applyRadioGroupName, buildClasses, escapeJSX,
renderNode, repeatString
- tailwind-renderer: applyRadioGroupName, escapeHtml, renderNode
Tests:
- registry.test.ts updated to use a deliberately-bogus type for the
unknown-type branch (previously used `text`, now migrated).
Verification:
- pnpm typecheck: clean
- pnpm test: 1,180 / 1,180 passing
- pnpm review:refresh: zero snapshot drift ("log unchanged")
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes the strangler-fig migration of all renderable node types
into self-contained modules under src/nodes/. The three legacy
renderers shrink from 2,214 lines to 383 lines (83% reduction); each
now consists of a thin registry-first dispatcher plus a fall-through
"Unknown node" comment for the unhandled types whose fixtures expect
that path.
Migrated this commit:
- Forms (6): input, textarea, select, checkbox, radio, radio-group.
radio-group preserves the radio-group counter mutation pattern via
the now-exported nextRadioGroupName helper.
- UI (7): badge, separator, comment, breadcrumbs (HTML-only),
tabs (HTML-only, includes inline tabs script), tab (HTML-only),
demo (HTML-only). React/Tailwind targets are intentionally absent
on HTML-only nodes — the dispatcher falls through and emits the
"Unknown node" comment those fixtures snapshot.
Contract change:
- nodes/_contract.ts — render targets are now optional. Many node
types historically had no React/Tailwind renderer; instead of
forcing every module to implement no-op stubs, the dispatcher just
falls through to the "Unknown node" comment which preserves the
snapshot bytes.
State + meta nodes (alert, accordion, accordion-item, breadcrumb-item,
loading-state, empty-state, error-state, form, option) had no
dedicated renderers in the legacy code — they hit the default arm.
Skipped: nothing to migrate.
Tests:
- registry.test.ts assertion weakened from "all three render targets
exist" to "type matches and at least one render target exists" to
reflect the optional-targets contract.
Verification:
- pnpm typecheck: clean
- pnpm test: 1,180 / 1,180 passing
- pnpm review:refresh: zero snapshot drift ("log unchanged")
- 17 KNOWN_FAILURES still .fails
Stats:
- 35+ node types migrated total (button, container, layout family,
content family, forms family, UI family).
- src/nodes/: 136 files, ~30 node-type folders.
- src/renderer/{html,react,tailwind}-renderer.ts: 200 + 112 + 71 lines.
- Adding a new component is now ~"drop a folder + register it".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the parse → AST → dispatch → render flow now that the monolith split has landed. Includes a one-glance "adding a new component" recipe pointing at src/nodes/<type>/ folders so future contributors land in the right place. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
teezeit
added a commit
that referenced
this pull request
May 1, 2026
…ecture (#80) ## Summary Both files still described the pre-split flow ("add a case to html-renderer.ts, react-renderer.ts, tailwind-renderer.ts") even though those switches were deleted in #77 / #78 / #79. New contributors following the old checklist would land in the wrong place. **No code change** — pure docs. ### CLAUDE.md - Architecture tree shows the new `nodes/<type>/` layout, the registry + contract files, and the per-theme styles split. - New "Render flow (Strategy + Registry)" paragraph explains the dispatcher → registry → node module flow and the intentional "Unknown node" fall-through for nodes without per-target renderers. - New "Adding a new component" pointer to the CONTRIBUTING checklist. - Classic tests list adds `registry.test.ts` and `styles-split.test.ts` (added in #77). ### CONTRIBUTING.md "Feature Development Checklist" - Replaced the three "add a case" bullets with one **Node module** bullet pointing at `src/nodes/<your-type>/`. - Notes that React and Tailwind targets are **optional** — many UI nodes (tabs, breadcrumbs, demo) ship HTML-only and the dispatcher falls through cleanly. - CSS bullet points at `renderer/styles/_structural.ts` and the per-theme files instead of the old monolithic `styles.ts`. - Removed the "Renderer index" bullet — `renderer/index.ts` no longer needs per-component plumbing. ## Test plan - [x] No code touched; nothing to test. - [ ] Skim both files for accuracy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
teezeit
added a commit
that referenced
this pull request
May 1, 2026
## Summary Self-contained brief for the next architectural beat after the renderer split. **No code change** — just the planning doc at `.github/dev-docs/parser-contract-plan.md`. The renderer monolith is split (#77 #78 #79 #80). The next high-leverage move is **not** splitting `parser/transformer.ts` per file — it's fixing the contract: explicit `TransformContext` with `peekNext`/`consumeNext`, plus isolated per-node parse tests that don't go through the full markdown→AST→render pipeline. The doc explains why (the file split is cosmetic; the leverage is the contract), what to build (`_context.ts` + test helpers + per-node tests), and what NOT to do (no file split in this PR, no syntax change, no bug fix). It also captures the user's pending syntax direction (`((pill))` over `|pill|`, `:::columns` over `:::grid`, etc.) so the next session has the destination clear before starting work. Two syntax ideas got pushed back during discussion and are flagged for design calls before any code: - Free-floating `{.right}/{.center}/{.left}` "applies until resolved" — stateful-parsing trap; counter-proposed "applies to nearest element only." - Navbar `[[ a | b | c ]]` `|` separators — collides with table syntax, new escape rules; counter-proposed alignment classes on items. ## Test plan - [x] No code touched. - [ ] Skim the doc for accuracy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Final PR of the monolith split. All node types with renderable output now live in self-contained modules under
packages/core/src/nodes/<type>/. The three legacy renderer files shrink from 2,214 lines to 383 lines (83% reduction) — each is now a thin registry-first dispatcher with a fall-through "Unknown node" comment for types whose fixtures expect that path.Adding a new component is now: drop a folder, register it, write fixtures.
What's migrated (this PR + earlier squash-merged PRs #77 #78)
Skipped intentionally: alert, accordion, accordion-item, breadcrumb-item, loading-state, empty-state, error-state, form, option — these had no dedicated renderers in the legacy code (fall through to default), so there's nothing to migrate.
Contract change
NodeDefinition.render.{html,react,tailwind}are now optional. Many node types historically had no React/Tailwind renderer — instead of forcing every module to implement no-op stubs, the dispatcher falls through to the "Unknown node" comment, preserving snapshot bytes exactly.Stats
Plus: 1 small chore commit fixing the CRLF fixture's gitattributes rule (was showing as modified on every branch due to autocrlf interaction).
Test plan
pnpm typecheckcleanpnpm test— 1,180 / 1,180 passingpnpm review:refresh— zero snapshot drift ("log unchanged"). Byte-equivalence with the legacy switches is proven.KNOWN_FAILURESstill.fails— refactor neither fixes nor breaks parser bugs.What's next (separate PRs, not this one)
nodes/<type>/folder, register it, add fixtures."transformer.tscan be tackled with a clean slate. The 17KNOWN_FAILURESare in scope for a future cleanup pass.transformer.tsper node type). The renderer split is the hard half; the parser is mostly mechanical from here.🤖 Generated with Claude Code