q2 preview: implement render-components (parent half) — no longer hub-client-only - #604
Merged
Merged
Conversation
Move hub-client's TSX transpiler (@babel/standalone) into @quarto/preview-renderer/utils/tsxTranspiler and extract its inline render-components meta walk into utils/renderComponents (extractRenderComponentPaths), both unit-tested. hub-client now imports the shared modules; behavior is unchanged (its integration tests pass against the new import path). This gives the q2-preview SPA (Phase 2) the same transpiler + meta walk hub-client uses, instead of a parallel implementation. Part of GH #402 / bd-ue80chl0 (q2 preview silently ignores render-components). Plan: claude-notes/plans/2026-08-25-preview-spa-render-components.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The CLI preview SPA now implements the parent half of the custom-components pipeline that only hub-client had: read render-components from the rendered AST meta, resolve entries with the shared resolveComponentPath, read the synced .tsx sources via getFileContent, transpile with the shared @babel/standalone transpiler, and pass customComponentsCode to Q2PreviewIframe. Design points (plan Q1/Q3 decisions): - Re-transpilation is keyed on the path-list string + a dedicated tsxTick (bumped only for .tsx changes), so per-keystroke .qmd edits never run babel. The empty result is referentially stable so the iframe never re-posts LOAD_CUSTOM_COMPONENTS on ordinary edits. - The transpiler module is imported dynamically: babel lands in a lazy Vite chunk (2.9 MB raw / 664 KB gz) fetched only when a document lists components. Iframe chunk size is unchanged (no leak); main chunk grew 4 KB. - Component failures (missing file, transpile error) surface as render warnings in the diagnostics overlay — no more silent divergence. Shared-iframe fix (entry.tsx): LOAD_CUSTOM_COMPONENTS now repaints the cached last UPDATE_AST payload after rebuilding the registry, so a component (re)load with no AST change takes effect immediately (live .tsx edits repaint; benefits hub-client too). Boot-order LOAD before the first AST still renders nothing. Tests: 2 iframe integration tests (re-render contract), 6 SPA unit tests (key extraction, lazy-import guarantee, warnings), 5 SPA integration tests (wiring, Q1 cadence both directions, stable empty identity, overlay surfacing). Full preview-renderer + SPA suites green. Part of GH #402 / bd-ue80chl0. Plan: claude-notes/plans/2026-08-25-preview-spa-render-components.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New q2-preview-spa/e2e/render-components.spec.ts drives target/debug/q2 preview on the existing with-render-components smoke fixture: both user overrides shadow the built-ins (p.my-para, div.my-callout; no div.callout), and a disk edit of overrides.tsx live-repaints the preview with the re-transpiled component. 2 passed; basic-preview.spec still green. End-to-end record added to the plan. Part of GH #402 / bd-ue80chl0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sure q2 preview deck.qmd now syncs the deck's declared render-components .tsx files: resolve_single_file_deps reads the key from the parsed document meta (as_plain_text, so bare-string YAML works), resolves entries against the deck dir (leading `/` = the synthetic project root, per the path-resolution contract), and appends existing in-tree .tsx files to the text-dep channel — which both syncs them as text for the SPA parent half and enrolls them in the closure-scoped watcher. Missing, escaping, and non-.tsx entries drop fail-soft (the SPA already surfaces a missing component as a render warning). Without this, single-file mode had no dir walk to discover the .tsx and would have re-created GH #402's silent divergence one mode over. Tests: two new config.rs unit tests (TDD, RED first); new [single-file] e2e in render-components.spec.ts (previewServer harness gained a targetFile option) proving overrides fire and a disk .tsx edit live-repaints under the real binary. 10/10 single_file_deps unit tests, 3/3 e2e green. Part of GH #402 / bd-ue80chl0. Plan: claude-notes/plans/2026-08-25-preview-spa-render-components.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cargo xtask verify (full, WASM leg) passed: 13,426 Rust tests, all TS suites, hub-client build:all, SPA build. cargo xtask lint clean. Docs check: render-components is undocumented/experimental — nothing to update. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
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.
Fixes #402 (resolution #1: implement the feature in the CLI preview). Tracked internally as braid strand bd-ue80chl0. Plan with design decisions and measurements:
claude-notes/plans/2026-08-25-preview-spa-render-components.md.What
q2 previewnow loads user TSXrender-components:overrides, in both project and single-file mode — previously the entries were silently dropped (the parent half of the pipeline existed only in hub-client; the iframe half was already shared).How
Phase 1 — shared helpers (no behavior change). hub-client's
@babel/standalonetranspiler moved to@quarto/preview-renderer/utils/tsxTranspiler, and its inline meta walk extracted intoutils/renderComponents(extractRenderComponentPaths), both unit-tested. hub-client now imports the shared modules — same transpiler, one implementation, no drift between surfaces.Phase 2 — SPA parent half.
q2-preview-spareadsrender-componentsfrom the rendered AST meta, resolves entries with the sharedresolveComponentPath, reads the synced.tsxsources (already synced by hub discovery + thePreviewBroadwatcher), transpiles, and passescustomComponentsCodetoQ2PreviewIframe.dist/45 → 49 MB.tsxTick(bumped only on.tsxchanges) — per-keystroke.qmdedits never run babel, and the empty result is referentially stable soLOAD_CUSTOM_COMPONENTSis never re-posted on ordinary edits.entry.tsx):LOAD_CUSTOM_COMPONENTSnow repaints the cached last AST payload after rebuilding the registry, so live.tsxedits take effect immediately (benefits hub-client too; boot-order LOAD before the first AST still renders nothing).Phase 3 — single-file mode.
resolve_single_file_depsnow readsrender-componentsfrom the parsed doc meta and appends existing in-tree.tsxfiles to the text-dep channel — synced as text and enrolled in the closure-scoped watcher. Entries resolve against the deck dir; leading/means the synthetic project root (path-resolution contract). Missing/escaping/non-.tsxentries drop fail-soft.Testing (TDD throughout; every test RED first)
config.rstests (closure pickup; drops for missing/..//non-tsx).target/debug/q2binary in Chromium (q2-preview-spa/e2e/render-components.spec.ts, reusing the existingwith-render-componentssmoke fixture): overrides shadow built-ins (p.my-para,div.my-callout, nodiv.callout); a disk.tsxedit live-repaints; and the same holds under single-fileq2 preview index.qmd(harness gained atargetFileoption). 3/3 green.cargo xtask verify(WASM leg included): 13,426 Rust tests + all TS suites + hub-clientbuild:all— passed.cargo xtask lintclean.Note:
hub-client/changelog.mdis intentionally untouched — the hub-client diff is a pure refactor (import moves), and the changelog's header excludes refactors.🤖 Generated with Claude Code