Skip to content

refactor(core): scaffold node-registry + split styles.ts into per-theme files#77

Merged
teezeit merged 2 commits intomainfrom
split-scaffold
May 1, 2026
Merged

refactor(core): scaffold node-registry + split styles.ts into per-theme files#77
teezeit merged 2 commits intomainfrom
split-scaffold

Conversation

@teezeit
Copy link
Copy Markdown
Owner

@teezeit teezeit commented May 1, 2026

Summary

PR 1 of the monolith split. Foundation for the node-centric architecture:

  • Scaffold the NodeDefinition<K> contract + static registry under packages/core/src/nodes/. Empty for now — wired into the three renderers as registry-first dispatch with fall-through to the legacy switches. Behavior is unchanged.
  • Split styles.ts (3,900 lines) into one file per theme under packages/core/src/renderer/styles/: sketch, clean, wireframe, none, tailwind, material, brutal, plus shared structural CSS.

The plan: each subsequent PR migrates one or more node types into nodes/<type>/{parse,html,react,tailwind}.ts, deleting their case arms from the legacy files. Adding a new component eventually becomes "drop in a folder."

See .github/dev-docs/monolith-split-plan.md for the full arc; this PR matches its "Phase 1 — styles" plus the registry scaffold.

What this PR does NOT change

  • No node types migrated yet (registry is empty).
  • No public API changes.
  • No fixture/snapshot changes.
  • 17 KNOWN_FAILURES still .fails (refactor neither fixes nor breaks them).

Test plan

  • pnpm typecheck clean
  • pnpm test — 1,153 / 1,153 passing
  • pnpm review:refresh — zero snapshot drift ("log unchanged")
  • CI green
  • Smoke test the editor / docs site after merge

🤖 Generated with Claude Code

teezeit and others added 2 commits May 1, 2026 21:52
…me files

PR 1 of the monolith split. Lays the foundation for migrating each node
type into a self-contained module under packages/core/src/nodes/<type>/
and splits the 3,900-line styles.ts into one file per visual theme.

Scaffold:
- nodes/_contract.ts — NodeDefinition<K> contract with per-target
  render functions (HTML / React / Tailwind have genuinely different
  context shapes; modeling them as one would force a bad union).
  TransformContext exposes peekNext / consumeNext / parseAttributes
  for parser-side migrations.
- nodes/_registry.ts — empty static registry + getNodeDefinition lookup.
- nodes/_helpers.ts — re-exports for shared helpers used by node modules.
- Registry-first dispatch hooks added to html-renderer, react-renderer,
  tailwind-renderer. With the registry empty, behavior is unchanged —
  all paths fall through to the existing switches.

Styles split:
- renderer/styles/{sketch,clean,wireframe,none,tailwind,material,brutal}.ts
  — one file per theme, ~135–730 lines each.
- renderer/styles/_structural.ts — shared structural CSS for tabs, rows,
  comments, demo blocks (previously inline in getStyleCSS).
- renderer/styles/index.ts — dispatcher; exports getStyleCSS unchanged.
- renderer/styles.ts — thin re-export shim for the legacy import path.

Verification:
- pnpm typecheck: clean
- pnpm test: 1,153 / 1,153 passing
- pnpm review:refresh: zero snapshot drift ("log unchanged")
- 17 KNOWN_FAILURES still .fails (refactor neither fixes nor breaks them)

Public API (packages/core/src/index.ts) unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pin the invariants that the strangler-fig migration relies on so any
future PR that breaks them fails loudly:

- tests/registry.test.ts (8 tests): registry empty by default,
  getNodeDefinition returns undefined for unknown types, dispatchers
  prefer registry.render.{html,react,tailwind} over the legacy switch,
  per-target fall-through is independent (registering only html does
  not affect react/tailwind paths).
- tests/styles-split.test.ts (19 tests): each theme getter emits
  non-empty prefix-aware CSS, structural module is non-empty,
  getStyleCSS = structural + theme in that order, unknown style name
  falls back to sketch (legacy behavior preserved).

Suite goes 1153 → 1180 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@teezeit teezeit merged commit 8233985 into main May 1, 2026
8 checks passed
teezeit added a commit that referenced this pull request May 1, 2026
## 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)

- **Container family**: container (11 subtypes incl. sidebar-main
extractor as recursion validator)
- **Layout** (6): nav, nav-item, brand, grid, grid-item, row
- **Content** (14): heading, paragraph, text, image, icon, link, list,
list-item, table, table-header, table-row, table-cell, blockquote, code
- **Forms** (6): input, textarea, select, checkbox, radio, radio-group
- **UI** (7): badge, separator, comment, breadcrumbs (HTML-only), tabs
(HTML-only), tab (HTML-only), demo (HTML-only)
- Plus: button (PR #78), styles split (PR #77)

**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

| | Before | After |
|---|---|---|
| html-renderer.ts | 953 | 200 |
| react-renderer.ts | 638 | 112 |
| tailwind-renderer.ts | 623 | 71 |
| **total legacy** | **2,214** | **383** (-83%) |
| nodes/ files | 0 | 136 |
| node folders | 0 | ~30 |

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

- [x] `pnpm typecheck` clean
- [x] `pnpm test` — 1,180 / 1,180 passing
- [x] `pnpm review:refresh` — zero snapshot drift ("log unchanged").
Byte-equivalence with the legacy switches is proven.
- [x] 17 `KNOWN_FAILURES` still `.fails` — refactor neither fixes nor
breaks parser bugs.
- [x] Public API unchanged.
- [ ] CI green
- [ ] Smoke test: render a complex fixture in the editor; eyeball each
style.

## What's next (separate PRs, not this one)

- **CONTRIBUTING.md** update: replace the 13-step Feature Development
Checklist with "drop a `nodes/<type>/` folder, register it, add
fixtures."
- **Knock out parser bug clusters** now that the parser still in
`transformer.ts` can be tackled with a clean slate. The 17
`KNOWN_FAILURES` are in scope for a future cleanup pass.
- Optional: parser-side migration (split `transformer.ts` per node
type). The renderer split is the hard half; the parser is mostly
mechanical from here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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