diff --git a/.claude/rules/dev-tools/dependency-updates.md b/.claude/rules/dev-tools/dependency-updates.md new file mode 100644 index 00000000000..3f93ac33054 --- /dev/null +++ b/.claude/rules/dev-tools/dependency-updates.md @@ -0,0 +1,10 @@ +--- +description: "Bundled dependency version bumps (Deno, Pandoc, Dart Sass, Typst, esbuild, veraPDF)" +paths: + - "configuration" + - "src/command/check/check.ts" +--- + +# Bundled Dependency Updates + +When updating a bundled binary, follow `dev-docs/upgrade-dependencies.md`. For Pandoc, also follow `dev-docs/update-pandoc-checklist.md`. diff --git a/.claude/rules/formats/pandoc-templates.md b/.claude/rules/formats/pandoc-templates.md index 68c8d56e80c..f875c8b922d 100644 --- a/.claude/rules/formats/pandoc-templates.md +++ b/.claude/rules/formats/pandoc-templates.md @@ -1,12 +1,19 @@ --- paths: - "src/resources/formats/pdf/pandoc/**" + - "src/resources/formats/beamer/pandoc/**" - "src/resources/formats/typst/pandoc/**" + - "src/resources/formats/html/pandoc/**" + - "src/resources/formats/revealjs/pandoc/**" + - "src/resources/formats/asciidoc/pandoc/**" - "package/src/common/update-pandoc.ts" + - "dev-docs/update-pandoc-checklist.md" --- -# Pandoc Templates (LaTeX & Typst) +# Pandoc Templates (LaTeX, Typst, HTML, Reveal.js, AsciiDoc) For how Pandoc's LaTeX templates are copied into Quarto and restructured into a modular form (`latex.template`, `latex.common`, …), see `llm-docs/pandoc-quarto-latex-templates.md`. For the equivalent Typst template integration (`typst.template`, `template.typst`), see `llm-docs/pandoc-quarto-typst-templates.md`. + +When updating Pandoc templates, read `dev-docs/update-pandoc-checklist.md` for the per-format file list, reference-copy maintenance, and filename checks. diff --git a/.github/actions/README.md b/.github/actions/README.md new file mode 100644 index 00000000000..2503b1431cb --- /dev/null +++ b/.github/actions/README.md @@ -0,0 +1,10 @@ +# Composite Actions + +Reusable steps shared across workflows in this repo. See also `.github/workflows/actions/` for another set of composite actions (historical split, no functional difference). + +| Action | Purpose | +|--------|---------| +| [`build-dist-tarball`](build-dist-tarball/action.yml) | Builds and uploads a versioned Quarto distribution tarball from the current checkout. | +| [`cache-typst`](cache-typst/action.yml) | Configures caching for Typst packages. | +| [`docker`](docker/action.yml) | Builds and pushes a Quarto Docker container to the GitHub Container Registry. | +| [`merge-extension-tests`](merge-extension-tests/action.yml) | Copies test files from extension subtrees into the main test directories. | diff --git a/.github/workflows/actions/README.md b/.github/workflows/actions/README.md new file mode 100644 index 00000000000..991c7b7be0f --- /dev/null +++ b/.github/workflows/actions/README.md @@ -0,0 +1,13 @@ +# Composite Actions + +Reusable steps shared across workflows in this repo. See also `.github/actions/` for another set of composite actions (historical split, no functional difference). + +| Action | Purpose | +|--------|---------| +| [`amplitude`](amplitude/action.yml) | Sends an event to Amplitude. | +| [`archive`](archive/action.yml) | Archives a dependency binary to S3. | +| [`keychain`](keychain/action.yml) | Configures a P12 certificate into the keychain for signing. | +| [`pandoc-override`](pandoc-override/action.yml) | Installs a Pandoc release for smoke tests before S3 archival and selects it through `QUARTO_PANDOC`. | +| [`prevent-rerun`](prevent-rerun/action.yml) | Fails if the workflow is re-run (`github.run_attempt > 1`). | +| [`quarto-dev`](quarto-dev/action.yml) | Configures the image for Quarto development (runs `configure.sh`/`.cmd`). | +| [`sign-files`](sign-files/action.yml) | Installs and configures the environment to sign files. | diff --git a/.github/workflows/actions/pandoc-override/action.yml b/.github/workflows/actions/pandoc-override/action.yml new file mode 100644 index 00000000000..e350d4b8702 --- /dev/null +++ b/.github/workflows/actions/pandoc-override/action.yml @@ -0,0 +1,48 @@ +name: "Pandoc Override" +description: "Installs a Pandoc release for smoke tests before S3 archival and selects it through QUARTO_PANDOC. If pandoc-version is empty, Quarto uses the configured binary." + +inputs: + pandoc-version: + description: "Pandoc release tag to install and use instead of the archived/configured version (e.g. 3.10.1). Leave empty to skip." + required: false + default: "" + +runs: + using: "composite" + steps: + - name: Install Pandoc override + if: inputs.pandoc-version != '' + uses: r-lib/actions/setup-pandoc@v2 + with: + pandoc-version: ${{ inputs.pandoc-version }} + + - name: Point QUARTO_PANDOC at override (Linux, macOS) + if: runner.os != 'Windows' && inputs.pandoc-version != '' + shell: bash + run: echo "QUARTO_PANDOC=$(command -v pandoc)" >> "$GITHUB_ENV" + + - name: Point QUARTO_PANDOC at override (Windows) + if: runner.os == 'Windows' && inputs.pandoc-version != '' + shell: pwsh + run: | + "QUARTO_PANDOC=$((Get-Command pandoc).Source)" >> $env:GITHUB_ENV + + # Lets check-versions-no-mismatch.test.ts tell an intentional override + # apart from a real bundled-version mismatch. Passed via env so the + # dispatch input is never interpolated into the shell script. + - name: Record expected Pandoc version for the version-mismatch test + if: inputs.pandoc-version != '' + shell: bash + env: + PANDOC_VERSION: ${{ inputs.pandoc-version }} + run: echo "QUARTO_TEST_EXPECTED_PANDOC=$PANDOC_VERSION" >> "$GITHUB_ENV" + + - name: Confirm Pandoc override is live (Linux, macOS) + if: runner.os != 'Windows' && inputs.pandoc-version != '' + shell: bash + run: quarto pandoc --version + + - name: Confirm Pandoc override is live (Windows) + if: runner.os == 'Windows' && inputs.pandoc-version != '' + shell: pwsh + run: quarto pandoc --version diff --git a/.github/workflows/test-smokes.yml b/.github/workflows/test-smokes.yml index b7a76360212..9db1df1b731 100644 --- a/.github/workflows/test-smokes.yml +++ b/.github/workflows/test-smokes.yml @@ -71,6 +71,11 @@ on: required: false type: string default: "" + pandoc-override-version: + description: "Pandoc release tag to download from GitHub for smoke tests before S3 archival (e.g. 3.10.1). Quarto selects it through QUARTO_PANDOC. Leave empty to use the configured binary." + required: false + type: string + default: "" # this is required to cleanup julia cache permissions: @@ -504,6 +509,12 @@ jobs: echo "QUARTO_TEST_EXPECTED_VERSION=$QUARTO_VERSION_INPUT" >> "$GITHUB_ENV" fi + # Guarded at the step so runs on older refs, which lack this action, never load it. + - if: inputs.pandoc-override-version != '' + uses: ./.github/workflows/actions/pandoc-override + with: + pandoc-version: ${{ inputs.pandoc-override-version }} + - name: Install Tinytex env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/dev-docs/update-pandoc-checklist.md b/dev-docs/update-pandoc-checklist.md index abc4206d779..bd33f84cb5a 100644 --- a/dev-docs/update-pandoc-checklist.md +++ b/dev-docs/update-pandoc-checklist.md @@ -1,13 +1,39 @@ ## Updating the bundled version of Pandoc -Carlos needs to run this: +Updating Pandoc happens in two phases. Phase 1 (prep) needs no S3 access and can be done by anyone; Phase 2 (archival) is done by whoever holds S3 write credentials, right before merge. -- [ ] Ensure archives are upgraded -- [ ] Run `AWS_PROFILE=... ./package/src/quarto-bld update-pandoc PANDOC_VERSION` -- [ ] look at `git diff`, specifically for changes in Pandoc templates, and adjust as needed. +### Phase 1 - prep (no S3, anyone) -As a reminder, our templates are kept in the same directories as Pandoc's templates, but with different names. `git diff` will show the diff in Pandoc's template; we have to manually patch -ours. (We can't just use `patch` because the templates have diverged too much) +Regenerate the reference copies and `src/core/pandoc/format-extension.ts`, port relevant changes to Quarto's templates, and verify rendering. Keep `configuration`'s `export PANDOC=` line at the archived version so `./configure.sh`/`.cmd` continues to work. + +Run the template/variant regeneration with `--skip-archive`, pointing `QUARTO_PANDOC` at an executable of the target version: + +```bash +QUARTO_PANDOC=/path/to/pandoc-3.10.1/bin/pandoc \ + ./package/src/quarto-bld update-pandoc 3.10.1 --skip-archive +``` + +`--skip-archive` skips S3 archival, the `configureDependency` download, and the `configuration` rewrite. It still runs `writePandocTemplates` to regenerate the mapped template files and `writeVariants` to regenerate `format-extension.ts`. + +`QUARTO_PANDOC` is required: `writeVariants` uses that binary to enumerate formats and extensions. The command checks its version before regenerating files. Review and commit the resulting working-tree diff. + +`writePandocTemplates` overwrites the reference copies and mapped Pandoc partials such as `fonts.latex` and `hypersetup.latex`. Port relevant changes to Quarto's templates using the [file list below](#pandoc-templates). Review generated changes to `format-extension.ts` as well. The same review applies to the full Phase 2 run. + +To smoke-test rendering before archival, dispatch `.github/workflows/test-smokes.yml` with `pandoc-override-version` set to the new release tag (e.g. `3.10.1`). The workflow downloads Pandoc from GitHub and uses it via `QUARTO_PANDOC` for that run, without S3 access. Limit `buckets` to the relevant smoke-all directories (e.g. `typst`, `latex`, `table`) for a faster run. + +For a full-suite run, the action sets `QUARTO_TEST_EXPECTED_PANDOC`. The version check accepts either OK or a strict-mismatch note for the requested version, normalized as in `check.ts`. + +### Phase 2 - archival (S3 credential holder, before merge) + +Whoever holds S3 write credentials does this last, right before merging: + +- [ ] Bump `configuration`'s `export PANDOC=` line and the `Pandoc` entry in `src/command/check/check.ts`'s `versionConstraints` array to the new version (this constraint tracks the bundled version exactly). +- [ ] Ensure archives are upgraded. +- [ ] Run `AWS_PROFILE=... ./package/src/quarto-bld update-pandoc PANDOC_VERSION` (without `--skip-archive`) to archive the binary to S3, configure it locally, and regenerate templates/variants. +- [ ] Review `git diff` for regenerated reference copies, mapped Pandoc partials, and `format-extension.ts`. Port any remaining upstream changes to Quarto's templates and reapply any Quarto changes overwritten in mapped partials. +- [ ] Run the `create-release.yml` dry-run signing/notarization gate (see `dev-docs/upgrade-dependencies.md`), then merge. + +Quarto's templates share directories with the Pandoc reference copies, but use different names. Review the reference-copy diffs and port relevant changes manually; the templates have diverged too much to use `patch`. ### Pandoc templates @@ -25,6 +51,36 @@ Partials: - Ours: - src/resources/formats/beamer/pandoc/common.latex +#### pdf / latex + +- Pandoc's: src/resources/formats/pdf/pandoc/latex.template +- Ours: src/resources/formats/pdf/pandoc/template.tex (see `llm-docs/pandoc-quarto-latex-templates.md` for the full partial breakdown, including `latex.common`/`common.latex`) + +#### html + +- Pandoc's: src/resources/formats/html/pandoc/html.template, src/resources/formats/html/pandoc/html.styles +- Ours: src/resources/formats/html/pandoc/template.html, src/resources/formats/html/pandoc/styles.html + +#### revealjs + +- Pandoc's: src/resources/formats/revealjs/pandoc/revealjs.template +- Ours: src/resources/formats/revealjs/pandoc/template.html + +#### asciidoc + +- Pandoc's: src/resources/formats/asciidoc/pandoc/asciidoc.template +- Ours: src/resources/formats/asciidoc/pandoc/template.asciidoc + +#### typst + +Typst keeps Pandoc's `template.typst` partial under its original name alongside the renamed `typst.template`. See `llm-docs/pandoc-quarto-typst-templates.md` for Quarto's partials. + +The files labelled "Pandoc's" are reference copies used to review upstream changes. Rendering uses the corresponding "Ours" files and their partials, such as `tables.tex` and `toc.tex`. + +- Regenerate reference copies from upstream with `writePandocTemplates`; apply rendering changes to Quarto's templates and partials. +- Keep reference copies identical to upstream, including any upstream typos. Manual edits are overwritten on regeneration. Fix upstream bugs in Quarto's templates. +- List the directory without filtering by extension when checking the files: `*.latex` excludes `latex.common`, which ends in `.common`. + ## Manual steps - [ ] Update schemas by inspecting [their changelog](https://github.com/jgm/pandoc/blob/main/changelog.md) for new commands, deprecation removals, etc diff --git a/dev-docs/upgrade-dependencies.md b/dev-docs/upgrade-dependencies.md index b51f82d1121..718eb98a918 100644 --- a/dev-docs/upgrade-dependencies.md +++ b/dev-docs/upgrade-dependencies.md @@ -1,6 +1,8 @@ Change version numbers in `./configuration` to correspond to new versions. -Update hardcoded version strings in `src/command/check/check.ts` (the `versionConstraints` array in `checkVersions`) so that they match the new versions in `configuration`. The `configuration` file warns about this in a comment, and `tests/smoke/check/check-versions-no-mismatch.test.ts` fails until they match. +Update hardcoded version strings in `src/command/check/check.ts` (the `versionConstraints` array in `checkVersions`) so that they match the new versions in `configuration`. The `configuration` file warns about this in a comment, and `tests/smoke/check/check-versions-no-mismatch.test.ts` fails until they match. The constraint tracks the bundled version exactly, not a minimum floor. + +For Pandoc, follow [update-pandoc-checklist.md](update-pandoc-checklist.md) for template updates and filename conventions, schema and Lua type checks, and CI smoke tests using `QUARTO_PANDOC` before S3 archival. ## Verify installer signing & notarization before merging (bundled binaries) diff --git a/llm-docs/localization-architecture.md b/llm-docs/localization-architecture.md index 0fe590714ae..e4d6fe904b9 100644 --- a/llm-docs/localization-architecture.md +++ b/llm-docs/localization-architecture.md @@ -16,14 +16,13 @@ key_files: - src/resources/filters/modules/authors.lua - src/resources/filters/modules/callouts.lua - src/resources/filters/layout/meta.lua - - src/resources/formats/html/pandoc/html.template + - src/resources/formats/html/pandoc/template.html + - src/resources/formats/html/pandoc/toc.html - src/resources/formats/html/pandoc/title-block.html - src/resources/formats/html/templates/title-metadata.html - - src/resources/formats/pdf/pandoc/latex.template - src/resources/formats/pdf/pandoc/babel-lang.tex - src/resources/formats/pdf/pandoc/toc.tex - - src/resources/formats/beamer/pandoc/beamer.template - - src/resources/formats/typst/pandoc/typst.template + - src/resources/formats/beamer/pandoc/toc.tex - src/resources/formats/typst/pandoc/quarto/typst-template.typ - src/resources/formats/typst/pandoc/quarto/typst-show.typ - src/resources/extension-subtrees/orange-book/_extensions/orange-book/typst-show.typ @@ -151,12 +150,14 @@ These bypass both Pandoc metadata and Lua filters. The string lands directly in `quarto.*` is an internal namespace. The user-facing override path for localized strings is the top-level `language:` YAML key (resolved by `formatLanguage`, already merged into `options.format.language` before the builder runs). The schema does not advertise `variables.quarto.*` as a user option, but the merge in `generateDefaults` honors a user-set value on collision: it deep-merges via `mergeConfigs` (`src/core/config.ts`) so user-supplied keys win at any nesting depth while all other localized values remain available. For example, a user setting `variables.quarto.language.crossref-ch-prefix: Bouquin` overrides only that one leaf — `$quarto.language.toc-title-document$` still resolves to the localized fallback. (Same helper is used in `src/core/language.ts:formatLanguage` to merge user-supplied `language:` onto `_language.yml` defaults.) A non-object `variables.quarto` (string, number, array) is ignored defensively. New contributors to this file should design around the `language:` path, not around the escape hatch. -From any Pandoc template — built-in (`html.template`, `latex.template`, `typst-template.typ`), extension partial, or custom user template — every localized key is then accessible as: +From any Pandoc template — built-in (`template.html`, `template.tex`, `typst-template.typ`), extension partial, or custom user template — every localized key is then accessible as: ```pandoc $quarto.language.$ ``` +> **Naming note**: Citations below refer to Quarto's rendering templates: `template.html` (HTML), `template.tex` (LaTeX/PDF and Beamer), and `quarto/typst-template.typ` (Typst). See `dev-docs/update-pandoc-checklist.md` for the corresponding Pandoc reference copies. + For example, `$quarto.language.crossref-ch-prefix$` resolves to `Chapitre` when `lang: fr` is set. Hyphens within the leaf segment are valid Pandoc template-variable syntax (manual: *"Variable names can include letters, numbers, underscores, hyphens, and periods"*). This is the channel of choice for new template consumers because: @@ -173,9 +174,9 @@ It does **not** replace 2a — Lua filters still need `param("key")` because Pan Localization paths used: -- **Document `lang` attribute**: `_language.yml` lookup not involved. `lang:` flows as Pandoc-native metadata, template renders `` (`html.template:2`). -- **TOC title**: `$toc-title$` resolved via channel 2b. Used in `html.template:60`, `toc.html:3`. -- **Abstract title**: `$abstract-title$` resolved via 2b. Used in `html.template:51`, `title-block.html:15`. +- **Document `lang` attribute**: `_language.yml` lookup not involved. `lang:` flows as Pandoc-native metadata, template renders `` (`template.html:2`). +- **TOC title**: `$toc-title$` resolved via channel 2b. Used in `toc.html:3`. +- **Abstract title**: `$abstract-title$` resolved via 2b. Used in `title-block.html:15`. - **Title block labels** (`Authors`, `Affiliations`, `Published`, `Modified`, `Doi`, `Abstract`, `Keywords`): `$labels.*$` written into meta by `modules/authors.lua:854-913` (`computeLabels`). Templates: `title-metadata.html:3,4,32,43,52,61,74,83`, `manuscript/title-metadata.html:6,7,37,48,57,66,83,92`. - **Crossref text** (`Figure 1.1`, `Table 2.1`): assembled in Lua by `crossref/format.lua` using `title()` / `refPrefix()` which call `param("crossref--title"/"-prefix")`. Written as inline text directly into the AST. By the time Pandoc renders HTML, the localized prefix is already document content. - **Callout titles** (`Tip`, `Note`, etc.): `modules/callouts.lua:15,185` reads `param("callout--title", default)`, writes into the callout node. @@ -191,7 +192,7 @@ Three localization paths layered: Other Lua filters use `metaInjectLatex` for non-language LaTeX customization (`crossref/custom.lua:78`, `layout/meta.lua`, `quarto-post/landscape.lua`, etc. — they inject packages or styling, not localized strings). -3. **Pandoc template `$var$`** — `pdf/pandoc/toc.tex:3` and `pdf/pandoc/latex.template:93-94` use `$toc-title$` to set `\contentsname`. Beamer templates (`beamer/pandoc/beamer.template:146-151`, `beamer/pandoc/toc.tex:3`) likewise. +3. **Pandoc template `$var$`** — `pdf/pandoc/toc.tex:2-3` uses `$toc-title$` to set `\contentsname`. Beamer's `beamer/pandoc/toc.tex:2-3` does the same. PDF-side TS code does not read `format.language` directly except `src/format/pdf/format-pdf.ts:242` which registers `"babel-lang"` as a Quarto partial. diff --git a/llm-docs/pandoc-quarto-latex-templates.md b/llm-docs/pandoc-quarto-latex-templates.md index c75e11f5d9b..31a007ef89b 100644 --- a/llm-docs/pandoc-quarto-latex-templates.md +++ b/llm-docs/pandoc-quarto-latex-templates.md @@ -1,3 +1,16 @@ +--- +main_commit: d30cdbb9e +analyzed_date: 2026-07-23 +key_files: + - package/src/common/update-pandoc.ts + - src/format/pdf/format-pdf.ts + - src/resources/formats/pdf/pandoc/template.tex + - src/resources/formats/pdf/pandoc/common.latex + - src/resources/formats/pdf/pandoc/pandoc.tex + - src/resources/formats/pdf/pandoc/document-metadata.latex + - src/resources/formats/beamer/pandoc/template.tex +--- + # Pandoc and Quarto LaTeX Templates This document describes how Quarto integrates Pandoc's LaTeX templates and transforms them into a modular structure suitable for Quarto's extended functionality. @@ -13,7 +26,7 @@ The `writePandocTemplates` function in `package/src/common/update-pandoc.ts` han ### Files Copied -Seven files are copied from Pandoc for LaTeX support: +The `templateDirFiles` mapping in `update-pandoc.ts` omits `document-metadata.latex`. Check `partialNamesPandoc` in `format-pdf.ts` for the complete list of Pandoc-derived partials. | Pandoc Source | Quarto Destination | Notes | | ----------------------------- | ------------------ | -------------------------------------------------- | @@ -24,6 +37,7 @@ Seven files are copied from Pandoc for LaTeX support: | `font-settings.latex` | (unchanged) | User font configuration | | `fonts.latex` | (unchanged) | Font engine detection | | `passoptions.latex` | (unchanged) | Package options passthrough | +| `document-metadata.latex` | (unchanged) | PDF standard metadata (PDF/A, PDF/UA, PDF/X); added in Quarto 1.7 following Pandoc 3.6.3 support; **not copied by `update-pandoc.ts`'s current mapping** | ### Purpose of Each Pandoc File @@ -41,6 +55,8 @@ Seven files are copied from Pandoc for LaTeX support: **passoptions.latex**: Package options passthrough mechanism for hyperref, xcolor, and xeCJK. +**document-metadata.latex**: Emits a `\DocumentMetadata{...}` block (LuaLaTeX, 2023+ LaTeX) for PDF/A, PDF/UA, and PDF/X standards support, driven by the `pdfstandard` variable. Called first, before `passoptions.latex()`, in the pdf `template.tex`. **Beamer does not use this partial** — its `template.tex` has no `document-metadata.latex()` call, and `format-pdf.ts` filters it out of `beamerPartialNamesPandoc`. + ## 2. Quarto's Modular Template Structure Quarto breaks the Pandoc templates into a modular structure in `src/resources/formats/pdf/pandoc/`. This allows for: @@ -68,7 +84,7 @@ Assembles the document by including all partials in order. This is Quarto's main Quarto maintains two versions of the common preamble: -**latex.common** (264 lines): Pandoc's original, kept as reference. Contains the full common preamble: paragraph formatting, verbatim, highlighting, tables, graphics, strikeout, CSL citations, Babel, page style, tight lists, subfigure support, text direction, natbib/biblatex configuration. +**latex.common** (265 lines): Pandoc's original, kept as reference. Contains the full common preamble: paragraph formatting, verbatim, highlighting, tables, graphics, strikeout, CSL citations, Babel, page style, tight lists, subfigure support, text direction, natbib/biblatex configuration. **common.latex** (66 lines): Quarto's shortened version that: @@ -77,6 +93,8 @@ Quarto maintains two versions of the common preamble: This allows Quarto to selectively override specific parts of the common preamble while maintaining compatibility with Pandoc updates. +Pandoc's `common.latex` is copied as `latex.common`; Quarto maintains its own `common.latex`. See `dev-docs/update-pandoc-checklist.md` for reference-copy maintenance and filename checks. + ### Quarto Partials (\*.tex) **Document Structure:** @@ -120,37 +138,38 @@ The `template.tex` orchestrates the full document: ### Preamble (before `\begin{document}`) -1. `$passoptions.latex()$` - Package options passthrough -2. `$doc-class.tex()$` - `\documentclass[...]{...}` declaration -3. (inline) - beamerarticle, xcolor, geometry, amsmath -4. (inline) - Section numbering configuration -5. `$fonts.latex()$` - Font engine detection (ifPDFTeX, ifXeTeX, ifLuaTeX) -6. `$font-settings.latex()$` - User font configuration -7. `$common.latex()$` - Common preamble setup, which internally calls: +1. `$document-metadata.latex()$` - `\DocumentMetadata{...}` for PDF/A, PDF/UA, PDF/X (pdf only — beamer's `template.tex` omits this call) +2. `$passoptions.latex()$` - Package options passthrough +3. `$doc-class.tex()$` - `\documentclass[...]{...}` declaration +4. (inline) - beamerarticle, xcolor, geometry, amsmath +5. (inline) - Section numbering configuration +6. `$fonts.latex()$` - Font engine detection (ifPDFTeX, ifXeTeX, ifLuaTeX) +7. `$font-settings.latex()$` - User font configuration +8. `$common.latex()$` - Common preamble setup, which internally calls: - `$pandoc.tex()$` → `$tables.tex()$`, `$graphics.tex()$`, `$citations.tex()$`, `$babel-lang.tex()$`, `$tightlist.tex()$`, `$biblio-config.tex()$` -8. `$after-header-includes.latex()$` - Bookmark, URL packages -9. `$hypersetup.latex()$` - Hyperref configuration -10. `$before-title.tex()$` - Pre-title content -11. `$title.tex()$` - `\title{}`, `\author{}`, `\date{}` +9. `$after-header-includes.latex()$` - Bookmark, URL packages +10. `$hypersetup.latex()$` - Hyperref configuration +11. `$before-title.tex()$` - Pre-title content +12. `$title.tex()$` - `\title{}`, `\author{}`, `\date{}` ### Document Body -12. `\begin{document}` -13. `$before-body.tex()$` - Frontmatter, `\maketitle`, abstract -14. (loop) - include-before content -15. `$toc.tex()$` - Table of contents -16. (inline) - linestretch, mainmatter -17. `$body$` - Document content -18. `$before-bib.tex()$` - Pre-bibliography content -19. (inline) - backmatter, nocite -20. `$biblio.tex()$` - Bibliography rendering -21. (loop) - include-after content -22. `$after-body.tex()$` - Post-body content -23. `\end{document}` +13. `\begin{document}` +14. `$before-body.tex()$` - Frontmatter, `\maketitle`, abstract +15. (loop) - include-before content +16. `$toc.tex()$` - Table of contents +17. (inline) - linestretch, mainmatter +18. `$body$` - Document content +19. `$before-bib.tex()$` - Pre-bibliography content +20. (inline) - backmatter, nocite +21. `$biblio.tex()$` - Bibliography rendering +22. (loop) - include-after content +23. `$after-body.tex()$` - Post-body content +24. `\end{document}` ## 4. TypeScript Configuration -The `pdfFormat` function in `src/format/pdf/format-pdf.ts` (lines 236-284) defines the template configuration: +The template configuration is defined inline inside `src/format/pdf/format-pdf.ts` (around lines 241-267, in the function that builds the PDF format): ```typescript // Quarto-maintained partials (.tex) @@ -176,6 +195,7 @@ const partialNamesQuarto: string[] = [ const partialNamesPandoc: string[] = [ "after-header-includes", "common", + "document-metadata", "font-settings", "fonts", "hypersetup", @@ -188,6 +208,8 @@ The template context maps these to files: - Quarto partials: `pandoc/${name}.tex` - Pandoc partials: `pandoc/${name}.latex` +Beamer builds its own `beamerPartialNamesPandoc` by filtering `document-metadata` out of `partialNamesPandoc` (`.filter((name) => name !== "document-metadata")`), since beamer's `template.tex` never calls it. + ## 5. Parameter Summary Table ### Font Parameters (XeTeX/LuaTeX) diff --git a/llm-docs/pandoc-quarto-typst-templates.md b/llm-docs/pandoc-quarto-typst-templates.md index c0182dd640c..a94cb9175ed 100644 --- a/llm-docs/pandoc-quarto-typst-templates.md +++ b/llm-docs/pandoc-quarto-typst-templates.md @@ -1,3 +1,15 @@ +--- +main_commit: d30cdbb9e +analyzed_date: 2026-07-23 +key_files: + - package/src/common/update-pandoc.ts + - src/format/typst/format-typst.ts + - src/resources/formats/typst/pandoc/quarto/definitions.typ + - src/resources/formats/typst/pandoc/quarto/template.typ + - src/resources/formats/typst/pandoc/typst.template + - src/resources/formats/typst/pandoc/template.typst +--- + # Pandoc and Quarto Typst Templates This document describes how Quarto integrates Pandoc's typst templates and transforms them into a more modular structure suitable for Quarto's extended functionality. @@ -172,9 +184,21 @@ Handles bibliography rendering: - Sets bibliography style from CSL file or bibliography style option - Renders bibliography from specified files -### typst.template - Reference Copy +### typst.template and template.typst - Reference Copies + +`typst.template` (Pandoc's `default.typst`) and `template.typst` (kept under its original name) are reference copies. `format-typst.ts` uses the `quarto/` template and partials described above for rendering. + +During a Pandoc update, review their diffs and port relevant changes to `quarto/definitions.typ` and the other Quarto partials. See `dev-docs/update-pandoc-checklist.md` for the regeneration and reference-copy maintenance procedure. + +### Writer-Level Changes vs. Template-Level Changes + +Pandoc's Typst writer converts the AST into body markup. Changes to its output can affect rendering regardless of the template used. + +For example, Pandoc 3.10.1 changed `HorizontalRule` output from `#horizontalrule` to `#divider()`, a native function added in Typst 0.15. The bundled Typst version is 0.15.1, so this call needs no local definition. Quarto's `quarto/definitions.typ` still defines `horizontalrule` for use by `notes.typ`. + +The same release changed definition-list output: descriptions are emitted directly rather than wrapped in `#block[...]`, relying on an external `#set terms(hanging-indent: ...)`. Quarto's `#show terms.item` rule reformats the description in either case. Neither change required a template change for Quarto. -This is the copy of Pandoc's `default.typst`, kept for reference. It is used when rendering without Quarto's template context (e.g., when a user provides a custom template). +When updating Pandoc, render a document with both versions using `QUARTO_PANDOC` and compare the generated `.typ` body content. Template diffs alone do not show changes in writer output. ## 3. Parameter Summary Table diff --git a/package/src/common/update-pandoc.ts b/package/src/common/update-pandoc.ts index 9b99c17ca6a..560394ca343 100644 --- a/package/src/common/update-pandoc.ts +++ b/package/src/common/update-pandoc.ts @@ -7,7 +7,7 @@ import { Command } from "cliffy/command/mod.ts"; import { join } from "../../../src/deno_ral/path.ts"; import { ensureDirSync } from "../../../src/deno_ral/fs.ts"; -import { info } from "../../../src/deno_ral/log.ts"; +import { error, info } from "../../../src/deno_ral/log.ts"; import { Configuration, @@ -33,61 +33,134 @@ import { import * as ld from "../../../src/core/lodash.ts"; +import { execProcess } from "../../../src/core/process.ts"; +import { pandocBinaryPath } from "../../../src/core/resources.ts"; + export function updatePandoc() { return new Command() .name("update-pandoc") .arguments("") .description("Updates Pandoc to the specified version") - .action(async (_args, version: string) => { + .option( + "--skip-archive", + "Regenerate templates and format-extension.ts without archiving to S3, configuring the binary, or updating configuration. Requires QUARTO_PANDOC to resolve to the requested version.", + ) + // deno-lint-ignore no-explicit-any + .action(async (options: any, version: string) => { info(`Updating Pandoc to ${version}`); const configuration = readConfiguration(); + const skipArchive = !!options.skipArchive; - // Update the configuration file - info(" updating configuration file."); - const configFilePath = join( - configuration.directoryInfo.root, - "configuration", - ); - const configText = Deno.readTextFileSync(configFilePath); - const configLines = lines(configText); - const outputLines: string[] = []; - for (const line of configLines) { - if (line.startsWith("export PANDOC=")) { - outputLines.push(`export PANDOC=${version}`); - } else { - outputLines.push(line); + if (skipArchive) { + // writeVariants() needs the target Pandoc binary to enumerate formats/extensions. + const overridePath = Deno.env.get("QUARTO_PANDOC"); + if (!overridePath) { + error( + `QUARTO_PANDOC is not set. Set it to a Pandoc ${version} executable ` + + "before using --skip-archive.", + ); + throw new Error("QUARTO_PANDOC is not set"); } - } - Deno.writeTextFileSync(configFilePath, outputLines.join("\n")); - const pandocDependency = pandoc(version); + // An unresolved QUARTO_PANDOC can fall back to the bundled binary. + // Verify the version before generating format-extension.ts. + let reported: string | undefined; + try { + reported = lines( + (await execProcess({ + cmd: pandocBinaryPath(), + args: ["--version"], + stdout: "piped", + })).stdout!, + )[0]?.split(" ")[1]; + } catch (_e) { + error( + "Could not run the QUARTO_PANDOC binary to verify its version. " + + `QUARTO_PANDOC is set to "${overridePath}". Confirm it points at a ` + + "runnable Pandoc executable.", + ); + throw new Error("QUARTO_PANDOC binary is not runnable"); + } + if ( + normalizePandocVersion(reported) !== normalizePandocVersion(version) + ) { + error( + `The Pandoc binary resolved via QUARTO_PANDOC reports version ` + + `${reported}, not the requested ${version}. QUARTO_PANDOC is set to ` + + `"${overridePath}". An unresolved override can fall back to the ` + + `bundled binary. Set QUARTO_PANDOC to a Pandoc ${version} executable ` + + "and retry.", + ); + throw new Error("QUARTO_PANDOC does not resolve to the requested version"); + } + } else { + info(" updating configuration file."); + const configFilePath = join( + configuration.directoryInfo.root, + "configuration", + ); + const configText = Deno.readTextFileSync(configFilePath); + const configLines = lines(configText); + const outputLines: string[] = []; + for (const line of configLines) { + if (line.startsWith("export PANDOC=")) { + outputLines.push(`export PANDOC=${version}`); + } else { + outputLines.push(line); + } + } + Deno.writeTextFileSync(configFilePath, outputLines.join("\n")); + } - // Call archive-bin-deps for this file await withWorkingDir(async (workingDir) => { - await archiveBinaryDependency(pandocDependency, workingDir); + if (!skipArchive) { + const pandocDependency = pandoc(version); - // Configure this version of pandoc - await configureDependency( - pandocDependency, - join(configuration.directoryInfo.bin, "tools"), - configuration, - ); + await archiveBinaryDependency(pandocDependency, workingDir); + + await configureDependency( + pandocDependency, + join(configuration.directoryInfo.bin, "tools"), + configuration, + ); + } - // Generate templates + // Templates are downloaded from Pandoc's source archive on GitHub. await writePandocTemplates(configuration, version, workingDir); - // Generate variants + // Generate format extensions using the resolved Pandoc binary, + // including any QUARTO_PANDOC override. await writeVariants(configuration); }); - // print the warning to complete the checklist - console.log(bgBlack(brightWhite(bold( - "\n** Remember to complete the checklist in /dev-docs/update-pandoc-checklist.md! **", - )))); + if (skipArchive) { + console.log(bgBlack(brightWhite(bold( + "\n** Templates and src/core/pandoc/format-extension.ts regenerated. **" + + "\n** Review `git diff` and port relevant upstream changes to Quarto's templates. **" + + "\n** Configuration is unchanged. Complete dev-docs/update-pandoc-checklist.md before merging. **", + )))); + } else { + console.log(bgBlack(brightWhite(bold( + "\n** Remember to complete the checklist in /dev-docs/update-pandoc-checklist.md! **", + )))); + } }); } +// Pad a Pandoc version to at least 3 parts so "3.10" matches "3.10.0". +// Longer versions keep every part: 3.1.6.1 and 3.1.6.2 are distinct releases. +function normalizePandocVersion(version: string | undefined): string { + if (!version) { + return ""; + } + const parts = version.split("."); + if (parts.length < 3) { + return parts.concat(Array(3 - parts.length).fill("0")).join("."); + } + return parts.join("."); +} + // Starting in Pandoc 3, we saw a number of variants that appear to be supported // disappear from the --list-extensions command, so for the time being we're just // hard adding them here diff --git a/tests/smoke/check/check-versions-no-mismatch.test.ts b/tests/smoke/check/check-versions-no-mismatch.test.ts index b080ab19c0e..326eee93ee0 100644 --- a/tests/smoke/check/check-versions-no-mismatch.test.ts +++ b/tests/smoke/check/check-versions-no-mismatch.test.ts @@ -5,21 +5,57 @@ * */ -import { testQuartoCmd } from "../../test.ts"; +import { testQuartoCmd, type Verify } from "../../test.ts"; import { printsMessage } from "../../verify.ts"; -// If this test fails, it indicates that there is a version mismatch -// Check 'src\command\check\check.ts' for the recorded versions, and compare to 'configuration' file +// For unexpected mismatches, compare versionConstraints in +// src/command/check/check.ts with configuration. + +// QUARTO_TEST_EXPECTED_PANDOC records an intentional override. Accept either +// OK or a strict-mismatch note for that version, normalized as in check.ts. +const expectedPandocOverride = Deno.env.get("QUARTO_TEST_EXPECTED_PANDOC"); + +// Mirrors the padding/truncation check.ts applies before its strict +// comparison, so a requested "3.10" matches the reported "3.10.0". +const normalizePandocVersion = (version: string): string => { + const parts = version.split("."); + if (parts.length > 3) { + return parts.slice(0, 3).join("."); + } + return parts.concat(Array(3 - parts.length).fill("0")).join("."); +}; + +const verifies: Verify[] = [ + printsMessage({ + level: "INFO", + regex: "(Dart Sass|Deno|Typst) version .*does not strictly match", + negate: true, + }), +]; + +if (expectedPandocOverride) { + verifies.push( + printsMessage({ + level: "INFO", + regex: `Pandoc version ${ + normalizePandocVersion(expectedPandocOverride).replaceAll(".", "\\.") + }(: OK| does not strictly match)`, + }), + ); +} else { + verifies.push( + printsMessage({ + level: "INFO", + regex: "Pandoc version .*does not strictly match", + negate: true, + }), + ); +} + testQuartoCmd( "check", ["versions"], - [ - printsMessage({ - level: "INFO", - regex: "does not strictly match", - negate: true // Verify this message does NOT appear - }) - ], + verifies, undefined, - "check versions --strict should not show version mismatches" + "check versions should report expected dependency versions", ); diff --git a/tools/runtime-boundary-baseline.json b/tools/runtime-boundary-baseline.json index cf99f8c67f6..fb072f47d04 100644 --- a/tools/runtime-boundary-baseline.json +++ b/tools/runtime-boundary-baseline.json @@ -12,11 +12,11 @@ "stdImportFiles": 1 }, "package/src": { - "denoCalls": 240, + "denoCalls": 241, "stdImportFiles": 3 }, "tests": { - "denoCalls": 774, + "denoCalls": 775, "stdImportFiles": 176 } }