diff --git a/news/changelog-1.11.md b/news/changelog-1.11.md index 7b15564dd0..19ed721f52 100644 --- a/news/changelog-1.11.md +++ b/news/changelog-1.11.md @@ -46,6 +46,7 @@ All changes included in 1.11: - ([#14772](https://github.com/quarto-dev/quarto-cli/issues/14772)): Custom `language` keys now reach templates through `$quarto.language.$`. A key that Quarto does not ship passed schema validation and was merged over the defaults, then discarded before templates saw it, so it expanded to an empty string with no warning. (author: @mcanouil) - ([#14882](https://github.com/quarto-dev/quarto-cli/issues/14882)): Fix brand font families with a word starting with a digit, such as `Source Sans 3`, not applying in `html` and `revealjs` output. The same names in `mainfont` now also apply in `revealjs`. (author: @Masad791) +- ([#14891](https://github.com/quarto-dev/quarto-cli/issues/14891)): Fix a brand file that holds only comments, such as a commented-out `_brand.yml` template, stopping the render with `YAML value is missing`. It now prints a warning and is ignored. (author: @Masad791) ### `dashboard` diff --git a/src/project/project-shared.ts b/src/project/project-shared.ts index 1eac8e6cc0..c6c3acfa62 100644 --- a/src/project/project-shared.ts +++ b/src/project/project-shared.ts @@ -5,6 +5,7 @@ */ import { existsSync, safeRemoveSync } from "../deno_ral/fs.ts"; +import { warnOnce } from "../core/log.ts"; import { dirname, isAbsolute, @@ -553,7 +554,26 @@ export async function projectResolveBrand( project: ProjectContext, fileName?: string, ): Promise { - async function loadSingleBrand(brandPath: string): Promise { + // A brand file holding only comments (e.g. a commented-out template) has no + // YAML value and would fail validation, so warn and treat it as absent. + function isEmptyBrandFile(brandPath: string): boolean { + let empty = false; + try { + empty = parse(Deno.readTextFileSync(brandPath)) == null; + } catch { + // invalid YAML: let validation report it + } + if (empty) { + warnOnce(`Brand file ${brandPath} has no content and will be ignored.`); + } + return empty; + } + async function loadSingleBrand( + brandPath: string, + ): Promise { + if (isEmptyBrandFile(brandPath)) { + return undefined; + } const brand = await readAndValidateYamlFromFile( brandPath, refSchema("brand-single", "Format-independent brand configuration."), @@ -563,7 +583,10 @@ export async function projectResolveBrand( } async function loadUnifiedBrand( brandPath: string, - ): Promise { + ): Promise { + if (isEmptyBrandFile(brandPath)) { + return undefined; + } const brand = await readAndValidateYamlFromFile( brandPath, refSchema("brand-unified", "Format-independent brand configuration."), @@ -662,7 +685,8 @@ export async function projectResolveBrand( if (!existsSync(brandPath)) { continue; } - project.brandCache.brand = await loadUnifiedBrand(brandPath); + project.brandCache.brand = (await loadUnifiedBrand(brandPath)) ?? + project.brandCache.brand; } return project.brandCache.brand; } else { diff --git a/tests/docs/smoke-all/2026/10/13/issue-14891/_brand.yml b/tests/docs/smoke-all/2026/10/13/issue-14891/_brand.yml new file mode 100644 index 0000000000..0b2bb55096 --- /dev/null +++ b/tests/docs/smoke-all/2026/10/13/issue-14891/_brand.yml @@ -0,0 +1,5 @@ +# A brand file that only holds comments, e.g. a commented-out template. +# color: +# primary: "#447099" +# typography: +# base: Inter diff --git a/tests/docs/smoke-all/2026/10/13/issue-14891/index.qmd b/tests/docs/smoke-all/2026/10/13/issue-14891/index.qmd new file mode 100644 index 0000000000..3b4eca0cae --- /dev/null +++ b/tests/docs/smoke-all/2026/10/13/issue-14891/index.qmd @@ -0,0 +1,13 @@ +--- +title: "Comment-only _brand.yml" +format: html +_quarto: + tests: + html: + noErrors: true + printsMessage: + level: WARN + regex: '_brand\.yml has no content and will be ignored' +--- + +A `_brand.yml` holding only comments should warn and be ignored, not stop the render.