Skip to content

feat(preview): single-file deck transitive include + image closure (bd-9cyza5vy)#300

Merged
cscheid merged 2 commits into
mainfrom
feature/bd-9cyza5vy-single-file-preview-transitive-deps
Jun 17, 2026
Merged

feat(preview): single-file deck transitive include + image closure (bd-9cyza5vy)#300
cscheid merged 2 commits into
mainfrom
feature/bd-9cyza5vy-single-file-preview-transitive-deps

Conversation

@cscheid

@cscheid cscheid commented Jun 17, 2026

Copy link
Copy Markdown
Member

What

Single-file q2 preview deck.qmd (no _quarto.yml) previously synced only the deck, a sibling _brand.yml, and the deck's direct images into the preview VFS. So {{< include part.qmd >}} rendered as a literal ?include placeholder, and any image referenced inside an included file was missing — single-file preview diverged from both q2 render and project-mode preview.

This resolves the deck's full transitive static dependency closure natively — without walking the directory (preserving the bd-tnm3k safety property) — by running the renderer's own ParseDocumentStage + IncludeExpansionStage against the real filesystem and reading back what it consumed:

  • includes from DocumentAst::recorded_includes (transitive, cycle-truncated)
  • images from collect_referenced_asset_urls over the expanded AST, deck-dir-anchored — matching render's "no path retargeting" design

Reusing the real stages (rather than a parallel walker) keeps preview path resolution in exact lock-step with render, including a latent nested-include retargeting bug (filed separately as bd-udrn0q47), which preview now inherits automatically instead of hard-coding one side of a contested rule.

Changes

  • quarto-preview config.rs: resolve_single_file_deps -> SingleFileDeps (supersedes + deletes resolve_single_file_assets); build_hub_config makes one resolver call filling both channels.
  • quarto-hub discovery.rs + context.rs: included .qmd ride a new invisible text path — HubConfig.single_file_text_depsProjectFiles.text_dep_files + with_text_deps (in text_files()/all_files()/counts, not qmd_files, mirroring resource_files). The single-file preview SPA has no file list, so included files stay VFS-only dependencies.
  • quarto-hub watch.rs + server.rs: extend the single-file watch set to the resolved closure (WatchConfig.single_file_deps) so editing an included file or referenced image re-renders, while unrelated siblings still never surface (bd-tnm3k preserved).

Tests

TDD throughout:

  • 6 resolver tests: include + image-in-include, transitive, deck-dir anchoring (render parity), cycle termination, under-root guard, deck-own direct image.
  • discovery + context: invisible-text-dep invariants (synced as text, absent from qmd_files, in the index).
  • watcher: closure dep surfaces while an unrelated sibling is ignored.
  • cargo xtask verify --skip-hub-build passes (clippy -D warnings + full-workspace nextest). The fix is native-only; WASM/quarto-core unchanged.

E2E

Browser inspection of q2 preview main.qmd (no _quarto.yml) where main.qmd includes part.qmd which references inc-image.png: the include expands ("Included Section" renders, no literal ?include), and the image inside the include displays via a blob: URL (naturalWidth 1, the 1×1 PNG loaded).

Notes

  • Render-side bug bd-udrn0q47 (nested-include path retargeting vs the documented "no retargeting" design) is filed but intentionally not addressed here — it's a separate render change this resolver will inherit.
  • Design + research: claude-notes/research/2026-06-16-include-shortcode-path-resolution.md, claude-notes/plans/2026-06-16-single-file-preview-transitive-deps.md.

🤖 Generated with Claude Code

cscheid and others added 2 commits June 16, 2026 19:10
…closure (bd-9cyza5vy)

Single-file `q2 preview deck.qmd` (no `_quarto.yml`) previously synced only
the deck, a sibling `_brand.yml`, and the deck's *direct* images into the
preview VFS. So `{{< include part.qmd >}}` rendered as a literal `?include`
placeholder, and any image referenced *inside* an included file was missing —
single-file preview diverged from both `q2 render` and project-mode preview.

Resolve the deck's full transitive static dependency closure natively, without
walking the directory (preserving the bd-tnm3k safety property), by running the
renderer's OWN `ParseDocumentStage` + `IncludeExpansionStage` against the real
filesystem and reading back what it consumed:

- includes from `DocumentAst::recorded_includes` (transitive, cycle-truncated)
- images from `collect_referenced_asset_urls` over the *expanded* AST,
  deck-dir-anchored — matching render's "no path retargeting" design

Reusing the real stages (rather than a parallel walker) keeps preview path
resolution in exact lock-step with render, including the latent nested-include
retargeting bug (filed separately as bd-udrn0q47), which preview now inherits
automatically.

Plumbing:
- quarto-preview/config.rs: `resolve_single_file_deps -> SingleFileDeps`
  (supersedes + deletes `resolve_single_file_assets`); `build_hub_config` makes
  one resolver call filling both channels.
- quarto-hub/discovery.rs + context.rs: included `.qmd` ride a new INVISIBLE
  text path — `HubConfig.single_file_text_deps` -> `ProjectFiles.text_dep_files`
  + `with_text_deps` (in `text_files()`/`all_files()`/counts, NOT `qmd_files`,
  mirroring the `resource_files` pattern). The single-file preview SPA has no
  file list, so included files stay VFS-only dependencies.
- quarto-hub/watch.rs + server.rs: extend the single-file watch set to the
  resolved closure (`WatchConfig.single_file_deps`) so editing an included file
  or referenced image re-renders, while unrelated siblings still never surface.

Tests (TDD): 6 resolver tests (include + image-in-include, transitive,
deck-dir anchoring/render parity, cycle, under-root guard, deck-own image);
discovery + context invisible-text-dep invariants; watcher closure-dep surfaces
while unrelated sibling is ignored. E2E: browser render of `q2 preview main.qmd`
shows the include expanded and the in-include image displayed via blob URL.

Docs: claude-notes/research/2026-06-16-include-shortcode-path-resolution.md
(the "no retargeting" design + the bd-udrn0q47 discrepancy, confirmed against
Quarto 1 source) and the implementation plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cscheid cscheid merged commit a2de7dd into main Jun 17, 2026
5 checks passed
@cscheid cscheid deleted the feature/bd-9cyza5vy-single-file-preview-transitive-deps branch June 17, 2026 02:04
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