Repository navigation
Fix double-escaped ampersands in Markdown image alt and title attributes - #18086
Merged
Princesseuh merged 1 commit intoSep 21, 2026
Merged
Conversation
The __ASTRO_IMAGE_ round-trip JSON-serializes an image's properties into a markup attribute and decodes them again when the final <img> is rendered. The decode step only handled the quote character references the Markdown processors emit, so an ampersand in an alt or title survived as entity text and was escaped a second time at render: `A & B` came out as `A & B`. Decode the ampersand references too, in both styles — numeric from remark, named from satteri — ampersands last, so a literal entity in the operator's own text is not mistaken for one the serializer wrote.
🦋 Changeset detectedLatest commit: a564573 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
1 task done
Merged
dadezzz
pushed a commit
to dadezzz/university_notes
that referenced
this pull request
Sep 27, 2026
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`7.3.3` → `7.3.4`](https://renovatebot.com/diffs/npm/astro/7.3.3/7.3.4) |  |  | --- ### Release Notes <details> <summary>withastro/astro (astro)</summary> ### [`v7.3.4`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#734) [Compare Source](https://github.com/withastro/astro/compare/astro@7.3.3...astro@7.3.4) ##### Patch Changes - [#​18063](withastro/astro#18063) [`40896ac`](withastro/astro@40896ac) Thanks [@​adamchal](https://github.com/adamchal)! - Fixes incremental builds repeatedly rendering unchanged pages when modules or compiled CSS reference bundled assets. - [#​18053](withastro/astro#18053) [`cf5d72f`](withastro/astro@cf5d72f) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Improves the `astro check` error shown for TypeScript 7. The command now explains that TypeScript 7 is not currently supported and provides instructions for experimentally type-checking Astro files with TypeScript 7.1 and `@astrojs/ts-content-mapper`. - [#​18086](withastro/astro#18086) [`795a7e4`](withastro/astro@795a7e4) Thanks [@​ump45nose](https://github.com/ump45nose)! - Fix double-escaped ampersands in Markdown image `alt` and `title` attributes. The `__ASTRO_IMAGE_` round-trip now decodes the numeric (`&`) and named (`&`) character references the Markdown processors emit, so an `&` in an alt or title is escaped exactly once in the final HTML instead of twice. - [#​18074](withastro/astro#18074) [`0429805`](withastro/astro@0429805) Thanks [@​SurefireStudios](https://github.com/SurefireStudios)! - Fix three error names that did not match their documented reference. `MissingLocale`, `MissingIndexForInternationalization` and `NoManifestAvailable` reported names ending in `Error` in the dev overlay, while their error reference pages are published under the unsuffixed names, so the name shown to users could not be found in the docs. - [#​18007](withastro/astro#18007) [`2245837`](withastro/astro@2245837) Thanks [@​L4XB](https://github.com/L4XB)! - Fixes the dev server re-evaluating the whole server module graph on every request. The `astro:head-metadata` plugin invalidated its component metadata virtual module from its own `transform` hook, so each evaluation of that module scheduled the next one. Adapters that run requests outside Vite's module runner, such as `@astrojs/cloudflare`, paid for a full re-evaluation of the server graph on every request for the lifetime of the process. - [#​18096](withastro/astro#18096) [`43657c4`](withastro/astro@43657c4) Thanks [@​matthewp](https://github.com/matthewp)! - Fixes domain-based i18n routing to respect `security.allowedDomains` when selecting a locale from request host headers - [#​18043](withastro/astro#18043) [`8a53a8b`](withastro/astro@8a53a8b) Thanks [@​astro-factory](https://github.com/apps/astro-factory)! - Fixes `image.responsiveStyles` emitting invalid `object-position` CSS values for same-axis keyword pairs (`top bottom`, `left right`, etc.) - [#​18029](withastro/astro#18029) [`c08252d`](withastro/astro@c08252d) Thanks [@​matthewp](https://github.com/matthewp)! - Runs `astro dev` and `astro preview` in the foreground when an AI agent is detected on Windows, allowing the agent to manage the process lifetime. Pass `--background` explicitly to request an Astro-managed background process. Agent-inferred backgrounding remains enabled on other platforms. - Updated dependencies \[[`3fd16ee`](withastro/astro@3fd16ee), [`8358d59`](withastro/astro@8358d59)]: - [@​astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@0.4.2 </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMTUuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjExNS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
altandtitleattributes, e.g.renderedalt="A &amp; B".Root cause
Markdown images are round-tripped through a
__ASTRO_IMAGE_markup attribute:rehypeImagesJSON-serializes the image's properties (includingalt/title) into that attribute, and the final<img>is rendered later by decoding it again. The decode step only handled the quote character references the Markdown processors emit — remark writes"/', satteri writes"/'— so an ampersand in the JSON survived the decode as entity text (&or&) and was escaped a second time when the attribute was finally rendered.The decode chains in
vite-plugin-markdown/images.ts(3 sites — this code is inlined into the browser bundle, so it stays self-contained) andcontent/runtime.ts(1 site) now decode the ampersand references as well, in both styles, ampersands last: quotes are decoded first so that a literal"in the operator's own alt text is not mistaken for one the serializer wrote, and only then is&un-escaped. Literal entity text in the source is preserved.Testing
core-image-remark-imgattrfixture withand asserted via cheerio that the renderedalt/titleareA & B/C & D(cheerio returns the decoded DOM value, so a double escape shows up as a literal&).actual: 'A & B'), with it restored it passes.core-image*(215 pass),astro-markdown*(47 pass),units/content-collections/image-references(11 pass),units/content-layer/asset-imports(3 pass). All green..changeset/fix-image-attr-ampersand.md.Docs
No docs change needed — the documented behavior of image
alt/titleis unchanged; this restores it.Disclosure: implemented with AI assistance (Claude); the root-cause analysis, fix, and verification are mine and I can explain every line of the diff.