mirror of
https://github.com/withastro/astro.git
synced 2025-12-05 18:56:38 -06:00
fix: preserve HAST properties in image processing (#14902)
Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>
This commit is contained in:
5
.changeset/pink-experts-feel.md
Normal file
5
.changeset/pink-experts-feel.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@astrojs/markdown-remark': patch
|
||||
---
|
||||
|
||||
Prevents HAST-only props from being directly converted into HTML attributes
|
||||
@@ -2,6 +2,17 @@ import type { Properties, Root } from 'hast';
|
||||
import { visit } from 'unist-util-visit';
|
||||
import type { VFile } from 'vfile';
|
||||
|
||||
/**
|
||||
* HAST properties to preserve on the node
|
||||
*/
|
||||
const HAST_PRESERVED_PROPERTIES = [
|
||||
// HAST: className -> HTML: class
|
||||
'className',
|
||||
|
||||
// HAST: htmlFor -> HTML: for
|
||||
'htmlFor',
|
||||
];
|
||||
|
||||
export function rehypeImages() {
|
||||
return function (tree: Root, file: VFile) {
|
||||
if (!file.data.astro?.localImagePaths?.length && !file.data.astro?.remoteImagePaths?.length) {
|
||||
@@ -16,13 +27,13 @@ export function rehypeImages() {
|
||||
if (typeof node.properties?.src !== 'string') return;
|
||||
|
||||
const src = decodeURI(node.properties.src);
|
||||
let newProperties: Properties;
|
||||
let imageProperties: Properties;
|
||||
|
||||
if (file.data.astro?.localImagePaths?.includes(src)) {
|
||||
// Override the original `src` with the new, decoded `src` that Astro will better understand.
|
||||
newProperties = { ...node.properties, src };
|
||||
imageProperties = { ...node.properties, src };
|
||||
} else if (file.data.astro?.remoteImagePaths?.includes(src)) {
|
||||
newProperties = {
|
||||
imageProperties = {
|
||||
// By default, markdown images won't have width and height set. However, just in case another user plugin does set these, we should respect them.
|
||||
inferSize: 'width' in node.properties && 'height' in node.properties ? undefined : true,
|
||||
...node.properties,
|
||||
@@ -33,12 +44,21 @@ export function rehypeImages() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Separate HAST-only properties from image processing properties
|
||||
const hastProperties: Properties = {};
|
||||
for (const key of HAST_PRESERVED_PROPERTIES) {
|
||||
if (key in imageProperties) {
|
||||
hastProperties[key] = imageProperties[key];
|
||||
delete imageProperties[key];
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize or increment occurrence count for this image
|
||||
const index = imageOccurrenceMap.get(node.properties.src) || 0;
|
||||
imageOccurrenceMap.set(node.properties.src, index + 1);
|
||||
|
||||
// Set a special property on the image so later Astro code knows to process this image.
|
||||
node.properties = { __ASTRO_IMAGE_: JSON.stringify({ ...newProperties, index }) };
|
||||
node.properties = { ...hastProperties, __ASTRO_IMAGE_: JSON.stringify({ ...imageProperties, index }) };
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { before, describe, it } from 'node:test';
|
||||
import { visit } from 'unist-util-visit';
|
||||
import { createMarkdownProcessor } from '../dist/index.js';
|
||||
|
||||
describe('collect images', async () => {
|
||||
let processor;
|
||||
let processorWithHastProperties;
|
||||
|
||||
before(async () => {
|
||||
processor = await createMarkdownProcessor({ image: { domains: ['example.com'] } });
|
||||
processor = await createMarkdownProcessor({ image: { domains: ['example.com'] }, });
|
||||
processorWithHastProperties = await createMarkdownProcessor({
|
||||
rehypePlugins: [
|
||||
() => {
|
||||
return (tree) => {
|
||||
visit(tree, 'element', (node) => {
|
||||
if (node.tagName === 'img') {
|
||||
node.properties.className = ['image-class'];
|
||||
node.properties.htmlFor = 'some-id';
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should collect inline image paths', async () => {
|
||||
@@ -75,4 +91,16 @@ describe('collect images', async () => {
|
||||
assert.deepEqual(metadata.localImagePaths, ['./img.webp']);
|
||||
assert.deepEqual(metadata.remoteImagePaths, ['https://example.com/example.jpg']);
|
||||
});
|
||||
|
||||
it('should preserve className as HTML class attribute', async () => {
|
||||
const markdown = `Hello `;
|
||||
const fileURL = 'file.md';
|
||||
|
||||
const { code } = await processorWithHastProperties.render(markdown, { fileURL });
|
||||
|
||||
assert.equal(
|
||||
code,
|
||||
'<p>Hello <img class="image-class" for="some-id" __ASTRO_IMAGE_="{"src":"./img.png","alt":"image with class","index":0}"></p>',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user