diff --git a/news/changelog-1.11.md b/news/changelog-1.11.md index 25b754954a..7484892f3e 100644 --- a/news/changelog-1.11.md +++ b/news/changelog-1.11.md @@ -79,6 +79,7 @@ All changes included in 1.11: - ([#14974](https://github.com/quarto-dev/quarto-cli/issues/14974)): Fix math in `.llms.md` files from `llms-txt` coming out garbled with the default `mathjax` method, and with `katex` and `webtex`. Math is now written as `$...$` and `$$...$$` with the `mathjax`, `katex`, `webtex` and `mathml` methods. - ([#15004](https://github.com/quarto-dev/quarto-cli/issues/15004)): Fix notebook preview pages and notebook sources missing from the output directory when a website with embedded notebooks is rendered from a directory other than the project directory. - ([#15022](https://github.com/quarto-dev/quarto-cli/issues/15022)): Fix rendering a listing with a `feed` failing on Windows with `os error 123` when a listing item's `image` or the feed `image` is a web URL. The image URL is used as is in the feed and no local size lookup is attempted. (author: @Masad791) +- ([#15032](https://github.com/quarto-dev/quarto-cli/pull/15032)): Fix the listing feed `image` missing its height and width when a website is rendered from a directory other than the project directory, or when the image path starts with `/`. ## Lua API diff --git a/src/project/types/website/listing/website-listing-feed.ts b/src/project/types/website/listing/website-listing-feed.ts index d33e09d59b..af0f12abbc 100644 --- a/src/project/types/website/listing/website-listing-feed.ts +++ b/src/project/types/website/listing/website-listing-feed.ts @@ -175,7 +175,10 @@ export async function createFeed( link: link, url: absoluteUrl(siteUrl, image), }; - const size = isAbsoluteRef(image) ? undefined : imageSize(image); + // Read the size from the project file the url points at, not the cwd + const size = isAbsoluteRef(image) + ? undefined + : imageSize(join(project.dir, image)); if (size) { const [height, width] = feedImageSize(size.height, size.width); feed.image.height = height; diff --git a/tests/docs/listings-feed-image/.gitignore b/tests/docs/listings-feed-image/.gitignore new file mode 100644 index 0000000000..812977e5f7 --- /dev/null +++ b/tests/docs/listings-feed-image/.gitignore @@ -0,0 +1,4 @@ +/.quarto/ +_site/ + +**/*.quarto_ipynb diff --git a/tests/docs/listings-feed-image/_quarto.yml b/tests/docs/listings-feed-image/_quarto.yml new file mode 100644 index 0000000000..cbe2bc9d13 --- /dev/null +++ b/tests/docs/listings-feed-image/_quarto.yml @@ -0,0 +1,7 @@ +project: + type: website + +website: + title: "listings-feed-image" + site-url: https://example.com + image: feed.png diff --git a/tests/docs/listings-feed-image/archive/index.qmd b/tests/docs/listings-feed-image/archive/index.qmd new file mode 100644 index 0000000000..0b528523f4 --- /dev/null +++ b/tests/docs/listings-feed-image/archive/index.qmd @@ -0,0 +1,7 @@ +--- +title: "Feed image from the site" +listing: + feed: true +--- + +This listing sets no image, so the feed falls back to the site `image`. diff --git a/tests/docs/listings-feed-image/archive/post.qmd b/tests/docs/listings-feed-image/archive/post.qmd new file mode 100644 index 0000000000..3cafb5a31c --- /dev/null +++ b/tests/docs/listings-feed-image/archive/post.qmd @@ -0,0 +1,6 @@ +--- +title: "An archived post" +date: "2026-10-12" +--- + +A post listed by the archive feed. diff --git a/tests/docs/listings-feed-image/feed.png b/tests/docs/listings-feed-image/feed.png new file mode 100644 index 0000000000..219cc7a3aa Binary files /dev/null and b/tests/docs/listings-feed-image/feed.png differ diff --git a/tests/docs/listings-feed-image/index.qmd b/tests/docs/listings-feed-image/index.qmd new file mode 100644 index 0000000000..5f505a9660 --- /dev/null +++ b/tests/docs/listings-feed-image/index.qmd @@ -0,0 +1,9 @@ +--- +title: "Feed image from the listing option" +listing: + contents: posts + feed: + image: feed.png +--- + +The feed image comes from the listing `feed: image:` option. diff --git a/tests/docs/listings-feed-image/posts/post.qmd b/tests/docs/listings-feed-image/posts/post.qmd new file mode 100644 index 0000000000..f56fd13272 --- /dev/null +++ b/tests/docs/listings-feed-image/posts/post.qmd @@ -0,0 +1,6 @@ +--- +title: "A post" +date: "2026-10-12" +--- + +A post listed by the root feed. diff --git a/tests/docs/listings-feed-image/updates/index.qmd b/tests/docs/listings-feed-image/updates/index.qmd new file mode 100644 index 0000000000..c89fdb2ecb --- /dev/null +++ b/tests/docs/listings-feed-image/updates/index.qmd @@ -0,0 +1,8 @@ +--- +title: "Feed image with a leading slash" +listing: + feed: + image: /feed.png +--- + +The feed image is given relative to the project root with a leading `/`. diff --git a/tests/docs/listings-feed-image/updates/post.qmd b/tests/docs/listings-feed-image/updates/post.qmd new file mode 100644 index 0000000000..0d0bf45dc3 --- /dev/null +++ b/tests/docs/listings-feed-image/updates/post.qmd @@ -0,0 +1,6 @@ +--- +title: "An update" +date: "2026-10-12" +--- + +A post listed by the updates feed. diff --git a/tests/smoke/site/render-listing-feed-image.test.ts b/tests/smoke/site/render-listing-feed-image.test.ts new file mode 100644 index 0000000000..8ffeb3b9d6 --- /dev/null +++ b/tests/smoke/site/render-listing-feed-image.test.ts @@ -0,0 +1,40 @@ +/* + * render-listing-feed-image.test.ts + * + * Copyright (C) 2026 Posit Software, PBC + */ +import { join } from "../../../src/deno_ral/path.ts"; +import { removeIfExists } from "../../../src/core/path.ts"; +import { testQuartoCmd, Verify } from "../../test.ts"; +import { docs } from "../../utils.ts"; +import { ensureFileRegexMatches, noErrorsOrWarnings } from "../../verify.ts"; + +// The project is rendered from the test cwd, not from its own directory, so +// the feed image size must be read relative to the project, not the cwd. +const projectDir = docs("listings-feed-image"); +const outputDir = join(projectDir, "_site"); + +// feed.png is 600x400, scaled down to fit the RSS 144px maximum width. +const feedImage = [ + /\s*https:\/\/example\.com\/feed\.png<\/url>/, + /96<\/height>\s*144<\/width>\s*<\/image>/, +]; + +const verify: Verify[] = [ + noErrorsOrWarnings, + // Listing `feed: image:` option + ensureFileRegexMatches(join(outputDir, "index.xml"), feedImage), + // Site `image` fallback + ensureFileRegexMatches(join(outputDir, "archive", "index.xml"), feedImage), + // Project-relative path with a leading slash + ensureFileRegexMatches(join(outputDir, "updates", "index.xml"), feedImage), +]; + +testQuartoCmd("render", [projectDir], verify, { + name: "Listing feed image size is read relative to the project", + teardown: () => { + removeIfExists(outputDir); + removeIfExists(join(projectDir, ".quarto")); + return Promise.resolve(); + }, +});