Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .claude/rules/testing/smoke-all-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,23 @@ _quarto:

Valid OS values: `linux`, `darwin`, `windows`

## Project Rendering (cross-file resolution)

Each `_quarto.tests` block renders only its own input file (`quarto render <input> --to <format>`). In a multi-file project (book/website) that leaves cross-file references resolved at the project post-render stage (e.g. an appendix cross-reference pointing into another chapter) unresolved.

Set `render-project: true` as a sibling of `tests:` (under `_quarto`) to render the whole project first:

```yaml
_quarto:
render-project: true
tests:
html:
ensureFileRegexMatches:
- ['>Whatever A<']
```

Gotcha: the project pre-render runs `quarto render <projectDir>` with **no `--to`**, so it builds only the formats **declared in `_quarto.yml`**. To test a format's cross-file output, that format must be declared in the project config — otherwise the pre-render skips it and the per-file render alone cannot resolve cross-file refs. Example: `tests/docs/smoke-all/2026/06/23/issue-11772` (book must declare `format: html` for the HTML appendix cross-reference test).

## Pattern Specificity

Avoid patterns that match template boilerplate instead of document content:
Expand Down
17 changes: 17 additions & 0 deletions llm-docs/testing-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,23 @@ Run test **without fix** first to verify it fails, then verify it passes with fi

Smoke-all tests embed test specifications directly in `.qmd` files using `_quarto.tests` metadata. See `.claude/rules/testing/smoke-all-tests.md` for full documentation.

### Project pre-render for cross-file resolution

A `_quarto.tests` block renders only its own input file (`quarto render <input> --to <format>`). In a multi-file project (book/website), cross-file references that are resolved at the project post-render stage — e.g. an appendix cross-reference in one chapter pointing at another chapter — stay unresolved under a single-file render.

Set `render-project: true` as a sibling of `tests:` to render the whole project first:

```yaml
_quarto:
render-project: true
tests:
html:
ensureFileRegexMatches:
- ['>Whatever A<']
```

The harness runs `quarto render <projectDir>` (in `smoke-all.test.ts`) before the per-file render. **Gotcha:** that pre-render has no `--to`, so it builds only the formats declared in `_quarto.yml`. To test a format's cross-file output, declare that format in the project config — otherwise the pre-render skips it and the per-file render alone cannot resolve cross-file references. Reference fixture: `tests/docs/smoke-all/2026/06/23/issue-11772` (the book declares `format: html` so the HTML appendix cross-reference resolves; the per-file HTML render then reuses the project crossref index).

### YAML String Escaping for Regex

**Critical rule:** In YAML single-quoted strings, `'\('` and `"\\("` are equivalent - both produce a literal `\(` in the regex.
Expand Down
4 changes: 4 additions & 0 deletions news/changelog-1.10.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ All changes included in 1.10:
- ([#14562](https://github.com/quarto-dev/quarto-cli/issues/14562)): Fix a heading inside `content-hidden when-format="llms-txt"` (visible in HTML) losing its `<section>` wrapper and `id` in a website with `llms-txt` enabled, which broke its table-of-contents link, anchors, and cross-references.
- ([#14563](https://github.com/quarto-dev/quarto-cli/issues/14563)): Fix a fatal error when a shortcode is used inside conditional content (e.g. `content-visible when-format="llms-txt"`) in a website with `llms-txt` enabled.

### Books

- ([#11772](https://github.com/quarto-dev/quarto-cli/issues/11772)): Fix `crossref.appendix-title` not being applied to appendix cross-references in PDF output. Appendix cross-references now use the configured title (e.g. `See Whatever A`) instead of always showing the default `Appendix` prefix.

## Commands

### `quarto preview`
Expand Down
24 changes: 18 additions & 6 deletions src/project/types/book/book-crossrefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
import { pathWithForwardSlashes } from "../../../core/path.ts";

import {
kCrossref,
kCrossrefAppendixTitle,
kCrossrefApxPrefix,
kCrossrefChapterId,
kCrossrefChapters,
Expand All @@ -37,7 +39,7 @@ import { WebsiteProjectOutputFile } from "../website/website.ts";
import { inputTargetIndex } from "../../project-index.ts";
import { bookConfigRenderItems } from "./book-config.ts";
import { isMultiFileBookFormat } from "./book-shared.ts";
import { Format } from "../../../config/types.ts";
import { Format, Metadata } from "../../../config/types.ts";

export async function bookCrossrefsPostRender(
context: ProjectContext,
Expand Down Expand Up @@ -305,11 +307,21 @@ function formatCrossref(
// if this is a section we need a prefix
const refNumber = numberOption(entry.order, options, type);
if (type === "sec" && !noPrefix) {
const prefix = (options[kCrossrefChapters] && isChapterRef(entry))
? options[kCrossrefChaptersAppendix]
? language[kCrossrefApxPrefix]
: language[kCrossrefChPrefix]
: language[kCrossrefSecPrefix];
let prefix;
if (options[kCrossrefChapters] && isChapterRef(entry)) {
if (options[kCrossrefChaptersAppendix]) {
// appendix cross-references prefer crossref.appendix-title (the same
// option that titles the appendix chapter heading) so the reference
// text matches the heading
const crossref = format.metadata?.[kCrossref] as Metadata | undefined;
prefix = (crossref?.[kCrossrefAppendixTitle] as string) ??
language[kCrossrefApxPrefix];
} else {
prefix = language[kCrossrefChPrefix];
}
} else {
prefix = language[kCrossrefSecPrefix];
}
const crossref = prefix + " " + refNumber;
return crossref;
} else {
Expand Down
8 changes: 8 additions & 0 deletions src/resources/filters/crossref/format.lua
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ function refPrefix(type, upper)
end
default = stringToInlines(default)
local prefix = crossrefOption(opt, default)
-- appendix cross-references prefer crossref.appendix-title (the same option that
-- titles the appendix chapter heading) so the reference text matches the heading
if type == "apx" then
local appendixTitle = crossrefOption("appendix-title")
if appendixTitle ~= nil then
prefix = appendixTitle
end
end
if upper then
local el = pandoc.Plain(prefix)
local firstStr = true
Expand Down
2 changes: 2 additions & 0 deletions tests/docs/smoke-all/2026/06/23/issue-11772/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.quarto/
**/*.quarto_ipynb
21 changes: 21 additions & 0 deletions tests/docs/smoke-all/2026/06/23/issue-11772/_quarto.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
project:
type: book

book:
title: "issue-11772"
author: "Norah Jones"
chapters:
- index.qmd
- intro.qmd
appendices:
- summary.qmd

format:
html: default
pdf:
documentclass: scrreprt
keep-tex: true

crossref:
appendix-title: "Whatever"
appendix-delim: ":"
19 changes: 19 additions & 0 deletions tests/docs/smoke-all/2026/06/23/issue-11772/index.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
format:
pdf:
keep-tex: true
_quarto:
tests:
pdf:
# crossref.appendix-title ("Whatever") must drive the appendix
# cross-reference prefix in PDF, like it does in HTML output.
# Currently the prefix falls back to the default "Appendix"
# (only language.crossref-apx-prefix is honored) — issue #11772.
ensureLatexFileRegexMatches:
- ['See Whatever~\\ref\{sec-summary\}']
- ['See Appendix~\\ref\{sec-summary\}']
---

# Preface {.unnumbered}

This is a book testing crossref appendix options in PDF (issue #11772).
17 changes: 17 additions & 0 deletions tests/docs/smoke-all/2026/06/23/issue-11772/intro.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
_quarto:
render-project: true
tests:
html:
# crossref.appendix-title ("Whatever") must drive the appendix
# cross-reference prefix in HTML too. This cross-file reference from a
# numbered chapter is resolved by the book post-render step, which
# previously only honored language.crossref-apx-prefix — issue #11772.
ensureFileRegexMatches:
- ['>Whatever A<']
- ['>Appendix A<']
---

# Introduction

See @sec-summary.
3 changes: 3 additions & 0 deletions tests/docs/smoke-all/2026/06/23/issue-11772/summary.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Summary {#sec-summary}

In summary, this book has no content whatsoever.
Loading