Fix book title-block template path being mangled by pandoc escaping - #14929
Merged
Merged
Conversation
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. |
cderv
force-pushed
the
fix/windows-binary-title-block-compile
branch
from
September 23, 2026 12:24
e81f704 to
c995a61
Compare
The book renderer hands the title-block template to book.lua by embedding the template's absolute path in a pandoc attribute on a generated code cell (template='...'). Pandoc's markdown reader resolves backslash escapes inside quoted attribute values, so a Windows path whose next segment starts with an ASCII punctuation character silently loses that separator when the generated markdown is read back: D:\a\_temp\...\share becomes D:\a_temp\...\share, the Lua side's io.open finds nothing, and the whole render aborts with "Error compiling template". Nothing exotic is needed to hit this - any install or checkout under a parent directory named _build, .local, -dev and so on reproduces it. The nightly Windows smoke leg started failing on the book fixtures because the runner unpacks quarto under D:\a\_temp. Escaping the value where it is produced, rather than converting the path to forward slashes, also covers a path containing a quote character. The regression test drives the generated markdown back through pandoc and compares the parsed attribute against the path that went in, so it exercises the actual round trip on every platform rather than asserting on the escaping itself.
cderv
force-pushed
the
fix/windows-binary-title-block-compile
branch
from
September 23, 2026 16:32
c995a61 to
3ac49ff
Compare
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 a book is rendered to a single-file format (
pdf,docx,epub) and Quarto sits under a path whose next segment starts with an ASCII punctuation character, the render aborts withError compiling template: ...\projects\book\pandoc\title-block.mdand pandoc exits 83.Root Cause
src/project/types/book/book-render.tshands the title-block template toquarto-post/book.luaby embedding the template's absolute path in a pandoc attribute on a generated code cell (template='...'). Pandoc's markdown reader resolves backslash escapes inside quoted attribute values, so on Windows a backslash followed by punctuation is consumed:D:\a\_temp\quarto\share\...is read back asD:\a_temp\quarto\share\....compileTemplateinsrc/resources/filters/common/pandoc.luathen gets nothing fromio.openand callsfail().A backslash followed by a letter survives, which is why a dev checkout resolving to
...\src\resources\...never hits this and the failure first looked specific to the packaged binary. It is not: any install or checkout under a parent directory named_build,.local,-devand so on reproduces it, including with a dev build. The nightly Windows smoke leg surfaced it because the runner unpacks the binary underD:\a\_temp.Fix
Escape backslashes and quotes when the attribute value is written, through a new
pandocQuotedAttrValueinsrc/core/pandoc/pandoc-attr.ts. This mirrors what pandoc's own markdown writer does on the way out, whereescAttrinsideattrsToMarkdownescapes the quote character and the backslash; that logic lives in the Haskell writer and is not exposed to Lua or to callers generating markdown from outside.The regression test generates the markdown and reads it back with pandoc, asserting the parsed
templateattribute matches the path that went in, so it covers the round trip that actually breaks rather than the escaping helper in isolation.Alternatives considered
Normalizing the path with
pathWithForwardSlashes(aspandocMetadataPathalready does for paths handed to filters and includes) fixes the reported failure and follows existing convention, but it addresses only the backslash. A path containing an apostrophe stays broken, and it is broken worse and on every platform: the quote closes the attribute value early, the fenced block stops parsing as a code block, and pandoc degrades the whole thing to an inline code span holding the literal attribute text plus the chapter body.book.luathen never sees a.quarto-title-blockcell, so there is no error at all, just missing content./home/o'brien/...is enough to trigger it. Escaping covers both cases with one transform.Passing the template path as a filter param instead, the way
ipynbalready feeds the samecompileTemplate(param('ipynb-title-block'), set insrc/format/ipynb/format-ipynb.ts), would keep the path out of the document entirely and is arguably the better shape. It was left out of this change because it addsformatExtrasplumbing to the book project type for what is a constant resource path, and because it would not remove the need for an escaper:createMarkdownTitle, a few lines away in the same file, re-emits user-authored heading attributes as text and escapes only the double quote today. That site and the equivalent gap inpandocNativeStr, which omits the backslash from its escape chain, are tracked separately.