fix(images): Simpler logic for collecting images in Markdown (#6744)

This commit is contained in:
Erika 2023-04-04 10:21:13 +02:00 committed by GitHub
parent 366decbe33
commit a1a4f45b51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 42 deletions

View file

@ -0,0 +1,6 @@
---
'astro': patch
'@astrojs/markdown-remark': patch
---
Fix remote images in Markdown throwing errors when using `experimental.assets`

View file

@ -86,18 +86,17 @@ export default function markdown({ settings, logging }: AstroPluginOptions): Plu
let html = renderResult.code; let html = renderResult.code;
const { headings } = renderResult.metadata; const { headings } = renderResult.metadata;
// Resolve all the extracted images from the content
let imagePaths: { raw: string; resolved: string }[] = []; let imagePaths: { raw: string; resolved: string }[] = [];
if (settings.config.experimental.assets) { if (settings.config.experimental.assets && renderResult.vfile.data.imagePaths) {
let paths = (renderResult.vfile.data.imagePaths as string[]) ?? []; for (let imagePath of renderResult.vfile.data.imagePaths.values()) {
imagePaths = await Promise.all( imagePaths.push({
paths.map(async (imagePath) => {
return {
raw: imagePath, raw: imagePath,
resolved: resolved:
(await this.resolve(imagePath, id))?.id ?? path.join(path.dirname(id), imagePath), (await this.resolve(imagePath, id))?.id ?? path.join(path.dirname(id), imagePath),
}; });
}) }
);
} }
const astroData = safelyGetAstroData(renderResult.vfile.data); const astroData = safelyGetAstroData(renderResult.vfile.data);

View file

@ -218,6 +218,19 @@ describe('astro:image', () => {
let $img = $('img'); let $img = $('img');
expect($img.attr('src').startsWith('/_image')).to.equal(true); expect($img.attr('src').startsWith('/_image')).to.equal(true);
}); });
it('properly handles remote images', async () => {
let res = await fixture.fetch('/httpImage');
let html = await res.text();
$ = cheerio.load(html);
let $img = $('img');
expect($img).to.have.a.lengthOf(2);
const remoteUrls = ['https://example.com/image.png', '/image.png'];
$img.each((index, element) => {
expect(element.attribs['src']).to.equal(remoteUrls[index]);
});
});
}); });
describe('getImage', () => { describe('getImage', () => {

View file

@ -0,0 +1,2 @@
![Remote image](https://example.com/image.png)
![/public image](/image.png)

View file

@ -9,9 +9,7 @@ export function rehypeImages() {
if (node.tagName !== 'img') return; if (node.tagName !== 'img') return;
if (node.properties?.src) { if (node.properties?.src) {
if (file.dirname) { if (file.data.imagePaths?.has(node.properties.src)) {
if (!isRelativePath(node.properties.src) && !isAliasedPath(node.properties.src)) return;
node.properties['__ASTRO_IMAGE_'] = node.properties.src; node.properties['__ASTRO_IMAGE_'] = node.properties.src;
delete node.properties.src; delete node.properties.src;
} }
@ -19,24 +17,3 @@ export function rehypeImages() {
}); });
}; };
} }
function isAliasedPath(path: string) {
return path.startsWith('~/assets');
}
function isRelativePath(path: string) {
return startsWithDotDotSlash(path) || startsWithDotSlash(path);
}
function startsWithDotDotSlash(path: string) {
const c1 = path[0];
const c2 = path[1];
const c3 = path[2];
return c1 === '.' && c2 === '.' && c3 === '/';
}
function startsWithDotSlash(path: string) {
const c1 = path[0];
const c2 = path[1];
return c1 === '.' && c2 === '/';
}

View file

@ -1,17 +1,31 @@
import type { Image } from 'mdast'; import type { Image } from 'mdast';
import { visit } from 'unist-util-visit'; import { visit } from 'unist-util-visit';
import type { VFile } from 'vfile'; import type { MarkdownVFile } from './types';
export default function toRemarkCollectImages() { export default function toRemarkCollectImages() {
return () => return () =>
async function (tree: any, vfile: VFile) { async function (tree: any, vfile: MarkdownVFile) {
if (typeof vfile?.path !== 'string') return; if (typeof vfile?.path !== 'string') return;
const imagePaths = new Set<string>(); const imagePaths = new Set<string>();
visit(tree, 'image', function raiseError(node: Image) { visit(tree, 'image', (node: Image) => {
imagePaths.add(node.url); if (shouldOptimizeImage(node.url)) imagePaths.add(node.url);
}); });
vfile.data.imagePaths = Array.from(imagePaths); vfile.data.imagePaths = imagePaths;
}; };
} }
function shouldOptimizeImage(src: string) {
// Optimize anything that is NOT external or an absolute path to `public/`
return !isValidUrl(src) && !src.startsWith('/');
}
function isValidUrl(str: string): boolean {
try {
new URL(str);
return true;
} catch {
return false;
}
}

View file

@ -1,8 +1,8 @@
import type * as hast from 'hast'; import type * as hast from 'hast';
import type * as mdast from 'mdast'; import type * as mdast from 'mdast';
import type { import type {
all as Handlers,
one as Handler, one as Handler,
all as Handlers,
Options as RemarkRehypeOptions, Options as RemarkRehypeOptions,
} from 'remark-rehype'; } from 'remark-rehype';
import type { ILanguageRegistration, IThemeRegistration, Theme } from 'shiki'; import type { ILanguageRegistration, IThemeRegistration, Theme } from 'shiki';
@ -85,11 +85,12 @@ export interface MarkdownMetadata {
export interface MarkdownVFile extends VFile { export interface MarkdownVFile extends VFile {
data: { data: {
__astroHeadings?: MarkdownHeading[]; __astroHeadings?: MarkdownHeading[];
imagePaths?: Set<string>;
}; };
} }
export interface MarkdownRenderingResult { export interface MarkdownRenderingResult {
metadata: MarkdownMetadata; metadata: MarkdownMetadata;
vfile: VFile; vfile: MarkdownVFile;
code: string; code: string;
} }