fix(preview): serve fresh HTML for project pages after .qmd edit#14548
Draft
cderv wants to merge 4 commits into
Draft
fix(preview): serve fresh HTML for project pages after .qmd edit#14548cderv wants to merge 4 commits into
cderv wants to merge 4 commits into
Conversation
In project preview mode, the persistent ProjectContext returned by watcher.project() owns a long-lived fileInformationCache populated at preview startup. When the watcher fires on a source edit, two render paths follow: the watcher itself dispatches to a render call that builds an ephemeral context (no stale cache), but the subsequent HTTP-handler render in serve.ts reuses the persistent context and reads the pre-edit expanded markdown back out of cache.fullMarkdown. The regenerated HTML mtime advances while the body content stays at the pre-edit revision (#10392). Invalidate cache entries for each changed input before the watcher's render dispatch, mirroring the renderForPreview pattern in src/command/preview/preview.ts. A companion commit adds a source-mtime and size fingerprint inside projectResolveFullMarkdownForFile so the cache contract self-validates even if a future caller forgets to invalidate; this surgical fix is the single-commit cherry-pick target for the v1.9 backport. Closes #10392.
The persistent ProjectContext used by website/book preview kept a fileInformationCache.fullMarkdown entry populated at startup with no freshness fingerprint, so subsequent renders fed Pandoc the pre-edit expanded markdown for the HTTP-handler renderProject() call site. Adds sourceMtime + sourceSize fields on FileInformation and re-reads when either differs from the cached value. Pairs with the watcher-side invalidation in the preceding commit; this layer closes the contract gap for any future caller that forgets to invalidate. Size is included alongside mtime to catch the edge case where an edit lands within a single mtime tick on a coarse-resolution filesystem but changes the byte count. Relates to #10392.
…#10392) Manual T1/T2/T3 (P1) plus T4-T6 (P2/P3) covering the project preview stale-render reproduction. Reproduces deterministically against the existing website fixture at tests/docs/manual/preview/project-preview/. Originally drafted on the debug/preview-cache-logging investigation branch; landing here so the spec accompanies the fix. Relates to #10392.
Collaborator
✅ 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.
When previewing a website or book project, editing a non-index
.qmdregenerated the HTML on disk but the preview server kept serving the pre-edit body. Stopping and restartingquarto previewcleared it for the first edit; the next edit reproduced the bug.Root Cause
watcher.project()returns a long-livedProjectContextwhosefileInformationCache.fullMarkdowncaches the expanded markdown per input. The HTTP-handler render insrc/project/serve/serve.tsreuses this context, so after a watcher-triggered re-render,projectResolveFullMarkdownForFilereturned the pre-edit expanded markdown. The regenerated HTML body therefore reflected the previous content.Fix
Two layers, motivated separately:
src/project/serve/watch.ts): invalidate the project context'sfileInformationCachefor each changed input inside thesubmitRendercallback. Inside the callback (not beforesubmitRender) so the cache mutation is serialized with any in-flight HTTP-handler render via the existing render queue —invalidateForFilemay delete a transient.quarto_ipynb, and running it before enqueue could race a concurrent read.src/project/project-shared.ts): guard thefullMarkdowncache entry by source filemtime + size. A stale entry is dropped on the next read even if a future caller forgets to invalidate. Size catches the case where filesystems with coarse mtime resolution (e.g. 2-second FAT/SMB) place a rapid edit in the same tick.Scope
This PR fixes the direct symptom for non-index project pages where the edit is to the
.qmdfile itself. Related cases raised in the issue thread are not covered here and are tracked separately:Prior reports on older release lines (#11475 on 1.6, #13755 on 1.8) describe the same root cause; this PR addresses it on
mainfor 1.10.Test Plan
Automated (
tests/unit/project/file-information-cache.test.ts): mtime guard, size guard, per-file invalidation.Manual spec:
tests/docs/manual/preview/10392-project-preview-stale-cache-after-edit.md..qmdin a website project — served HTML reflects the edit (T1)index.qmd— no regression on the index path (T3).quarto_ipynbaccumulation tests passBackport
Not included here. A v1.9.x backport will be considered alongside the next stable patch.
Fixes #10392