Skip to content

Commit e941869

Browse files
authored
Fix false citeproc warning from @ in book metadata URLs (#14105)
When a book project has URLs containing `@` in navbar config (e.g., `href: https://mastodon.online/@user`) and a bibliography, Pandoc emits false `[WARNING] Citeproc: citation not found` warnings because it parses YAML metadata strings as Markdown, interpreting `@user` as a citation reference. Book metadata can't be deleted before passing to Pandoc (unlike website metadata) because `{{< meta book.* >}}` shortcodes depend on it. Escape `@` to `&#64;` in URL strings within `book` metadata before writing the Pandoc metadata file. The escaping is scoped to URL strings only (via `isExternalPath()`), so non-URL metadata strings like intentional citations in description fields are left untouched. Pandoc resolves the HTML entity back to `@` in the AST, so templates and shortcodes see the original character. Fixes #12136
1 parent 1619596 commit e941869

9 files changed

Lines changed: 73 additions & 1 deletion

File tree

news/changelog-1.9.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ All changes included in 1.9:
131131

132132
### `book`
133133

134+
- ([#12136](https://github.com/quarto-dev/quarto-cli/issues/12136)): Fix false `citeproc: citation not found` warnings when book metadata URLs contain `@` (e.g., Mastodon links). The `@` in URL strings is now escaped before passing to Pandoc, preventing misinterpretation as citation references.
134135
- ([#13769](https://github.com/quarto-dev/quarto-cli/issues/13769)): Apply `repo-link-target` and `repo-link-rel` options to tools in book sidebar for consistent link attribute handling with website projects. (author: @mcanouil)
135136

136137
### `manuscript`

src/command/render/pandoc.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { Document } from "../../core/deno-dom.ts";
2727
import { execProcess } from "../../core/process.ts";
2828
import { dirAndStem, normalizePath } from "../../core/path.ts";
2929
import { mergeConfigs } from "../../core/config.ts";
30+
import { isExternalPath } from "../../core/url.ts";
3031

3132
import {
3233
Format,
@@ -1339,6 +1340,13 @@ export async function runPandoc(
13391340
}
13401341
}
13411342

1343+
// Escape @ in book metadata to prevent false citeproc warnings (#12136).
1344+
// Book metadata can't be deleted like website/about because {{< meta book.* >}}
1345+
// shortcodes depend on it. Pandoc resolves &#64; back to @ in the AST.
1346+
if (pandocPassedMetadata.book) {
1347+
pandocPassedMetadata.book = escapeAtInMetadata(pandocPassedMetadata.book);
1348+
}
1349+
13421350
Deno.writeTextFileSync(
13431351
metadataTemp,
13441352
stringify(pandocPassedMetadata, {
@@ -1833,3 +1841,21 @@ function resolveTextHighlightStyle(
18331841
}
18341842
return extras;
18351843
}
1844+
1845+
// deno-lint-ignore no-explicit-any
1846+
function escapeAtInMetadata(value: any): any {
1847+
if (typeof value === "string") {
1848+
return isExternalPath(value) ? value.replaceAll("@", "&#64;") : value;
1849+
}
1850+
if (Array.isArray(value)) {
1851+
return value.map(escapeAtInMetadata);
1852+
}
1853+
if (value !== null && typeof value === "object") {
1854+
const result: Record<string, unknown> = {};
1855+
for (const [k, v] of Object.entries(value)) {
1856+
result[k] = escapeAtInMetadata(v);
1857+
}
1858+
return result;
1859+
}
1860+
return value;
1861+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/.quarto/
2+
**/*.quarto_ipynb
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
project:
2+
type: book
3+
4+
book:
5+
title: "Test Issue 12136"
6+
navbar:
7+
right:
8+
- icon: mastodon
9+
href: https://mastodon.online/@testuser
10+
chapters:
11+
- index.qmd
12+
13+
bibliography: references.bib
14+
15+
format:
16+
html: default
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: "Test"
3+
_quarto:
4+
tests:
5+
html:
6+
printsMessage:
7+
level: INFO
8+
regex: 'Citeproc: citation'
9+
negate: true
10+
ensureHtmlElements:
11+
- ['a.nav-link[href="https://mastodon.online/@testuser"]']
12+
- []
13+
---
14+
15+
# Test {.unnumbered}
16+
17+
No false citeproc warnings from @ in book navbar URLs.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@misc{example,
2+
author = {Test Author},
3+
title = {Example},
4+
year = {2024}
5+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
/.quarto/
2+
3+
**/*.quarto_ipynb
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/.quarto/
2-
/_book/
2+
/_book/
3+
**/*.quarto_ipynb
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
/.quarto/
2+
3+
**/*.quarto_ipynb

0 commit comments

Comments
 (0)