diff --git a/news/changelog-1.11.md b/news/changelog-1.11.md index 4a4dd339fba..6f414e7b422 100644 --- a/news/changelog-1.11.md +++ b/news/changelog-1.11.md @@ -65,6 +65,7 @@ All changes included in 1.11: - ([#15115](https://github.com/quarto-dev/quarto-cli/pull/15115)): Fix Google Scholar writing two `citation_reference` tags for a reference defined in two bibliography files. The tag now describes the entry Pandoc renders. - ([#15118](https://github.com/quarto-dev/quarto-cli/pull/15118)): Fix Google Scholar metadata missing the first and last page of bibliography entries, and giving wrong pages for a citation `page` written with an en-dash or as a page list. - ([#15125](https://github.com/quarto-dev/quarto-cli/pull/15125)): Fix Google Scholar metadata failing the render with a CSL JSON (`.json`) bibliography. +- ([#15128](https://github.com/quarto-dev/quarto-cli/pull/15128)): Fix Google Scholar metadata missing references with a YAML bibliography not wrapped in `---` delimiters. ### `pdf` diff --git a/src/core/bibliography.ts b/src/core/bibliography.ts index 8c2ef439c43..46c6a586fcf 100644 --- a/src/core/bibliography.ts +++ b/src/core/bibliography.ts @@ -13,9 +13,23 @@ import { Metadata } from "../config/types.ts"; import { asArray } from "./array.ts"; import { CSL } from "./csl.ts"; +import { FAILSAFE_SCHEMA, loadAll, Type } from "yaml"; + import { execProcess } from "./process.ts"; import { pandocBinaryPath } from "./resources.ts"; +// Keeps every scalar as written and, like pandoc, accepts any tag (!!int, +// !custom, ...) by keeping the untagged value +const kBibliographyYamlSchema = FAILSAFE_SCHEMA.extend( + (["scalar", "sequence", "mapping"] as const).map((kind) => + new Type("", { + kind, + multi: true, + construct: (data: unknown) => data ?? "", + }) + ), +); + export async function bibliographyCslJson(input: string, metadata: Metadata) { const bibliography = metadata[kBibliography] as string | string[]; if (bibliography) { @@ -84,15 +98,36 @@ export async function renderToCSLJSON( // The bibliopath might end up as an absolute path // in which case just leave it alone (otherwise it is cwd relative) const biblioPath = join(dir, biblio); + const absBiblioPath = isAbsolute(biblioPath) + ? biblioPath + : join(Deno.cwd(), biblioPath); + const ext = extname(biblioPath).toLowerCase(); const cmd = [pandocBinaryPath()]; - // Without -f, pandoc reads a .json input as its own JSON AST - if (extname(biblioPath).toLowerCase() === ".json") { - cmd.push("-f"); - cmd.push("csljson"); + let stdin: string | undefined; + if (ext === ".json") { + // Without -f, pandoc reads a .json input as its own JSON AST + cmd.push("-f", "csljson", absBiblioPath); + } else if (ext === ".yaml" || ext === ".yml") { + // Pandoc has no reader for a YAML bibliography: read as markdown, it + // only works when wrapped in --- delimiters. Like --bibliography, use + // the first YAML document, either a references: map or a bare array. + // json: true lets a duplicate key win last, as pandoc does. + const firstDoc = loadAll(Deno.readTextFileSync(absBiblioPath), null, { + schema: kBibliographyYamlSchema, + json: true, + })?.[0]; + if (Array.isArray(firstDoc)) { + // --metadata-file needs a map, so wrap the array in a metadata block + // for pandoc's YAML reader to parse its fields as it would the map + cmd.push("-f", "markdown"); + stdin = `---\n${JSON.stringify({ references: firstDoc })}\n---\n`; + } else { + cmd.push("-f", "markdown", `--metadata-file=${absBiblioPath}`); + stdin = ""; + } + } else { + cmd.push(absBiblioPath); } - cmd.push( - isAbsolute(biblioPath) ? biblioPath : join(Deno.cwd(), biblioPath), - ); cmd.push("-t"); cmd.push("csljson"); cmd.push("--citeproc"); @@ -105,6 +140,7 @@ export async function renderToCSLJSON( stderr: "piped", cwd: dir, }, + stdin, ); if (result.success) { if (result.stdout) { diff --git a/tests/docs/smoke-all/2026/10/14/scholar-references/array-yaml.qmd b/tests/docs/smoke-all/2026/10/14/scholar-references/array-yaml.qmd new file mode 100644 index 00000000000..8e4b454a777 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/14/scholar-references/array-yaml.qmd @@ -0,0 +1,15 @@ +--- +title: Scholar metadata for a top-level array YAML bibliography +format: html +google-scholar: true +bibliography: references-array.yaml +_quarto: + tests: + html: + ensureHtmlElements: + - ['meta[name="citation_reference"][content*="citation_title=Formatted reference;"][content*="citation_volume=1.10;"][content*="citation_issue=7;"]'] +--- + +The bibliography is a YAML file holding a top-level list of references, with no `references:` key, which pandoc accepts for `--bibliography`. The fields still go through pandoc's YAML reader: the markdown title is parsed and the unquoted volume keeps its text. + +This article cites one work [@cited]. diff --git a/tests/docs/smoke-all/2026/10/14/scholar-references/bare-yaml.qmd b/tests/docs/smoke-all/2026/10/14/scholar-references/bare-yaml.qmd new file mode 100644 index 00000000000..2b3ea4bb648 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/14/scholar-references/bare-yaml.qmd @@ -0,0 +1,15 @@ +--- +title: Scholar metadata for a bare YAML bibliography +format: html +google-scholar: true +bibliography: references-bare.yaml +_quarto: + tests: + html: + ensureHtmlElements: + - ['meta[name="citation_reference"][content*="citation_author=Alice Smith;"]'] +--- + +The bibliography is a YAML file with a `references:` list and no `---` delimiters, which pandoc accepts for `--bibliography`. Pandoc has no input format that reads such a file, so the Scholar reference tags need it handled explicitly. + +This article cites one work [@cited]. diff --git a/tests/docs/smoke-all/2026/10/14/scholar-references/delimited-yaml.qmd b/tests/docs/smoke-all/2026/10/14/scholar-references/delimited-yaml.qmd new file mode 100644 index 00000000000..b95e11a6428 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/14/scholar-references/delimited-yaml.qmd @@ -0,0 +1,15 @@ +--- +title: Scholar metadata for a delimited YAML bibliography +format: html +google-scholar: true +bibliography: references-delimited.yaml +_quarto: + tests: + html: + ensureHtmlElements: + - ['meta[name="citation_reference"][content*="citation_title=Cited reference;"]'] +--- + +The bibliography is a YAML file wrapped in `---` delimiters, with a duplicated `title` key. Pandoc warns and keeps the last value, so the Scholar reference tags do too. + +This article cites one work [@cited]. diff --git a/tests/docs/smoke-all/2026/10/14/scholar-references/references-array.yaml b/tests/docs/smoke-all/2026/10/14/scholar-references/references-array.yaml new file mode 100644 index 00000000000..35444f8a1d0 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/14/scholar-references/references-array.yaml @@ -0,0 +1,11 @@ +- id: cited + type: article-journal + title: "*Formatted* reference" + volume: 1.10 + issue: !!int 7 + author: + - family: Smith + given: Alice + issued: + date-parts: + - [2024] diff --git a/tests/docs/smoke-all/2026/10/14/scholar-references/references-bare.yaml b/tests/docs/smoke-all/2026/10/14/scholar-references/references-bare.yaml new file mode 100644 index 00000000000..93f881d3b55 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/14/scholar-references/references-bare.yaml @@ -0,0 +1,10 @@ +references: +- id: cited + type: article-journal + title: Cited reference + author: + - family: Smith + given: Alice + issued: + date-parts: + - [2024] diff --git a/tests/docs/smoke-all/2026/10/14/scholar-references/references-delimited.yaml b/tests/docs/smoke-all/2026/10/14/scholar-references/references-delimited.yaml new file mode 100644 index 00000000000..2833ea54128 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/14/scholar-references/references-delimited.yaml @@ -0,0 +1,13 @@ +--- +references: +- id: cited + type: article-journal + title: Superseded title + title: Cited reference + author: + - family: Smith + given: Alice + issued: + date-parts: + - [2024] +--- diff --git a/tests/docs/smoke-all/2026/10/14/scholar-references/references-tagged.yaml b/tests/docs/smoke-all/2026/10/14/scholar-references/references-tagged.yaml new file mode 100644 index 00000000000..90368f2d9f7 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/14/scholar-references/references-tagged.yaml @@ -0,0 +1,14 @@ +--- +references: +- id: cited + type: article-journal + title: Cited reference + volume: !!int 2 + issue: !custom 7 + author: + - family: Smith + given: Alice + issued: + date-parts: + - [!!int 2024] +--- diff --git a/tests/docs/smoke-all/2026/10/14/scholar-references/tagged-yaml.qmd b/tests/docs/smoke-all/2026/10/14/scholar-references/tagged-yaml.qmd new file mode 100644 index 00000000000..3074a931319 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/14/scholar-references/tagged-yaml.qmd @@ -0,0 +1,15 @@ +--- +title: Scholar metadata for a YAML bibliography with explicit tags +format: html +google-scholar: true +bibliography: references-tagged.yaml +_quarto: + tests: + html: + ensureHtmlElements: + - ['meta[name="citation_reference"][content*="citation_publication_date=2024;"][content*="citation_volume=2;"][content*="citation_issue=7;"]'] +--- + +The bibliography is a delimited YAML file whose values carry explicit tags, standard (`!!int`) and custom (`!custom`). Pandoc accepts any tag and keeps the value's text, so the Scholar reference tags do too. + +This article cites one work [@cited].