summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/honest-snakes-peel.md5
-rw-r--r--packages/astro/src/assets/services/squoosh.ts6
-rw-r--r--packages/astro/src/assets/utils/emitAsset.ts6
-rw-r--r--packages/astro/src/assets/utils/metadata.ts16
-rw-r--r--packages/astro/src/assets/vite-plugin-assets.ts22
-rw-r--r--packages/astro/src/core/errors/errors-data.ts35
-rw-r--r--packages/astro/src/vite-plugin-markdown/index.ts49
7 files changed, 96 insertions, 43 deletions
diff --git a/.changeset/honest-snakes-peel.md b/.changeset/honest-snakes-peel.md
new file mode 100644
index 000000000..c9cf064f6
--- /dev/null
+++ b/.changeset/honest-snakes-peel.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix relative images in Markdown breaking the build process in certain circumstances
diff --git a/packages/astro/src/assets/services/squoosh.ts b/packages/astro/src/assets/services/squoosh.ts
index 4ba78f8d3..023e92fe5 100644
--- a/packages/astro/src/assets/services/squoosh.ts
+++ b/packages/astro/src/assets/services/squoosh.ts
@@ -29,8 +29,8 @@ const qualityTable: Record<
// Squoosh's PNG encoder does not support a quality setting, so we can skip that here
};
-async function getRotationForEXIF(inputBuffer: Buffer): Promise<Operation | undefined> {
- const meta = await imageMetadata(inputBuffer);
+async function getRotationForEXIF(inputBuffer: Buffer, src?: string): Promise<Operation | undefined> {
+ const meta = await imageMetadata(inputBuffer, src);
if (!meta) return undefined;
// EXIF orientations are a bit hard to read, but the numbers are actually standard. See https://exiftool.org/TagNames/EXIF.html for a list.
@@ -63,7 +63,7 @@ const service: LocalImageService = {
const operations: Operation[] = [];
- const rotation = await getRotationForEXIF(inputBuffer);
+ const rotation = await getRotationForEXIF(inputBuffer, transform.src);
if (rotation) {
operations.push(rotation);
diff --git a/packages/astro/src/assets/utils/emitAsset.ts b/packages/astro/src/assets/utils/emitAsset.ts
index 9b83a020a..b9ca146b7 100644
--- a/packages/astro/src/assets/utils/emitAsset.ts
+++ b/packages/astro/src/assets/utils/emitAsset.ts
@@ -22,11 +22,7 @@ export async function emitESMImage(
return undefined;
}
- const fileMetadata = await imageMetadata(fileData);
-
- if (!fileMetadata) {
- return undefined;
- }
+ const fileMetadata = await imageMetadata(fileData, id);
const emittedImage: ImageMetadata = {
src: '',
diff --git a/packages/astro/src/assets/utils/metadata.ts b/packages/astro/src/assets/utils/metadata.ts
index 7d7ee7457..fc89ca1ca 100644
--- a/packages/astro/src/assets/utils/metadata.ts
+++ b/packages/astro/src/assets/utils/metadata.ts
@@ -1,19 +1,23 @@
import probe from 'probe-image-size';
+import { AstroError, AstroErrorData } from '../../core/errors/index.js';
import type { ImageInputFormat, ImageMetadata } from '../types.js';
-export async function imageMetadata(data: Buffer): Promise<Omit<ImageMetadata, 'src'> | undefined> {
+export async function imageMetadata(
+ data: Buffer,
+ src?: string
+): Promise<Omit<ImageMetadata, 'src'>> {
const result = probe.sync(data);
+
if (result === null) {
- throw new Error('Failed to probe image size.');
+ throw new AstroError({
+ ...AstroErrorData.NoImageMetadata,
+ message: AstroErrorData.NoImageMetadata.message(src),
+ });
}
const { width, height, type, orientation } = result;
const isPortrait = (orientation || 0) >= 5;
- if (!width || !height || !type) {
- return undefined;
- }
-
return {
width: isPortrait ? height : width,
height: isPortrait ? width : height,
diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts
index 0fe45d1ab..9c95b6dc4 100644
--- a/packages/astro/src/assets/vite-plugin-assets.ts
+++ b/packages/astro/src/assets/vite-plugin-assets.ts
@@ -2,6 +2,8 @@ import MagicString from 'magic-string';
import type * as vite from 'vite';
import { normalizePath } from 'vite';
import type { AstroPluginOptions, ImageTransform } from '../@types/astro.js';
+import { extendManualChunks } from '../core/build/plugins/util.js';
+import { AstroError, AstroErrorData } from '../core/errors/index.js';
import {
appendForwardSlash,
joinPaths,
@@ -28,6 +30,18 @@ export default function assets({
// Expose the components and different utilities from `astro:assets` and handle serving images from `/_image` in dev
{
name: 'astro:assets',
+ outputOptions(outputOptions) {
+ // Specifically split out chunk for asset files to prevent TLA deadlock
+ // caused by `getImage()` for markdown components.
+ // https://github.com/rollup/rollup/issues/4708
+ extendManualChunks(outputOptions, {
+ after(id) {
+ if (id.includes('astro/dist/assets/services/')) {
+ return `astro-assets-services`;
+ }
+ },
+ });
+ },
async resolveId(id) {
if (id === VIRTUAL_SERVICE_ID) {
return await this.resolve(settings.config.image.service.entrypoint);
@@ -125,6 +139,14 @@ export default function assets({
}
if (assetRegex.test(id)) {
const meta = await emitESMImage(id, this.meta.watchMode, this.emitFile);
+
+ if (!meta) {
+ throw new AstroError({
+ ...AstroErrorData.ImageNotFound,
+ message: AstroErrorData.ImageNotFound.message(id),
+ });
+ }
+
return `export default ${JSON.stringify(meta)}`;
}
},
diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts
index 1f336e5f8..e4fe35540 100644
--- a/packages/astro/src/core/errors/errors-data.ts
+++ b/packages/astro/src/core/errors/errors-data.ts
@@ -620,8 +620,42 @@ export const ExpectedImageOptions = {
message: (options: string) =>
`Expected getImage() parameter to be an object. Received \`${options}\`.`,
} satisfies ErrorData;
+
+/**
+ * @docs
+ * @see
+ * - [Images](https://docs.astro.build/en/guides/images/)
+ * @description
+ * Astro could not find an image you imported. Often, this is simply caused by a typo in the path.
+ *
+ * Images in Markdown are relative to the current file. To refer to an image that is located in the same folder as the `.md` file, the path should start with `./`
+ */
+export const ImageNotFound = {
+ name: 'ImageNotFound',
+ title: 'Image not found.',
+ message: (imagePath: string) => `Could not find requested image \`${imagePath}\`. Does it exist?`,
+ hint: 'This is often caused by a typo in the image path. Please make sure the file exists, and is spelled correctly.',
+} satisfies ErrorData;
+
/**
* @docs
+ * @message Could not process image metadata for `IMAGE_PATH`.
+ * @see
+ * - [Images](https://docs.astro.build/en/guides/images/)
+ * @description
+ * Astro could not process the metadata of an image you imported. This is often caused by a corrupted or malformed image and re-exporting the image from your image editor may fix this issue.
+ */
+export const NoImageMetadata = {
+ name: 'NoImageMetadata',
+ title: 'Could not process image metadata.',
+ message: (imagePath: string | undefined) =>
+ `Could not process image metadata${imagePath ? ' for `${imagePath}`' : ''}.`,
+ hint: 'This is often caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue.',
+} satisfies ErrorData;
+
+/**
+ * @docs
+ * @deprecated This error is no longer Markdown specific and as such, as been replaced by `ImageNotFound`
* @message
* Could not find requested image `IMAGE_PATH` at `FULL_IMAGE_PATH`.
* @see
@@ -640,6 +674,7 @@ export const MarkdownImageNotFound = {
}`,
hint: 'This is often caused by a typo in the image path. Please make sure the file exists, and is spelled correctly.',
} satisfies ErrorData;
+
/**
* @docs
* @description
diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts
index 163baab0d..7d4a97392 100644
--- a/packages/astro/src/vite-plugin-markdown/index.ts
+++ b/packages/astro/src/vite-plugin-markdown/index.ts
@@ -12,7 +12,8 @@ import { normalizePath } from 'vite';
import type { AstroSettings } from '../@types/astro.js';
import { AstroError, AstroErrorData, MarkdownError } from '../core/errors/index.js';
import type { Logger } from '../core/logger/core.js';
-import { isMarkdownFile, rootRelativePath } from '../core/util.js';
+import { isMarkdownFile } from '../core/util.js';
+import { shorthash } from '../runtime/server/shorthash.js';
import type { PluginMetadata } from '../vite-plugin-astro/types.js';
import { escapeViteEnvReferences, getFileInfo } from '../vite-plugin-utils/index.js';
@@ -92,12 +93,13 @@ export default function markdown({ settings, logger }: AstroPluginOptions): Plug
const { headings, imagePaths: rawImagePaths, frontmatter } = renderResult.metadata;
// Resolve all the extracted images from the content
- const imagePaths: { raw: string; resolved: string }[] = [];
+ const imagePaths: { raw: string; resolved: string; safeName: string }[] = [];
for (const imagePath of rawImagePaths.values()) {
imagePaths.push({
raw: imagePath,
resolved:
(await this.resolve(imagePath, id))?.id ?? path.join(path.dirname(id), imagePath),
+ safeName: shorthash(imagePath),
});
}
@@ -118,39 +120,28 @@ export default function markdown({ settings, logger }: AstroPluginOptions): Plug
${layout ? `import Layout from ${JSON.stringify(layout)};` : ''}
import { getImage } from "astro:assets";
+ ${imagePaths.map((entry) => `import Astro__${entry.safeName} from ${JSON.stringify(entry.raw)};`)}
- export const images = {
- ${imagePaths.map(
- (entry) =>
- `'${entry.raw}': await getImageSafely((await import("${entry.raw}")).default, "${
- entry.raw
- }", "${rootRelativePath(settings.config.root, entry.resolved)}")`
- )}
- }
-
- async function getImageSafely(imageSrc, imagePath, resolvedImagePath) {
- if (!imageSrc) {
- throw new AstroError({
- ...AstroErrorData.MarkdownImageNotFound,
- message: AstroErrorData.MarkdownImageNotFound.message(
- imagePath,
- resolvedImagePath
- ),
- location: { file: "${id}" },
- });
+ const images = async function() {
+ return {
+ ${imagePaths
+ .map((entry) => `"${entry.raw}": await getImage({src: Astro__${entry.safeName}})`)
+ .join('\n')}
}
-
- return await getImage({src: imageSrc})
}
- function updateImageReferences(html) {
- return html.replaceAll(
- /__ASTRO_IMAGE_="([^"]+)"/gm,
- (full, imagePath) => spreadAttributes({src: images[imagePath].src, ...images[imagePath].attributes})
- );
+ async function updateImageReferences(html) {
+ return images().then((images) => {
+ return html.replaceAll(/__ASTRO_IMAGE_="([^"]+)"/gm, (full, imagePath) =>
+ spreadAttributes({
+ src: images[imagePath].src,
+ ...images[imagePath].attributes,
+ })
+ );
+ });
}
- const html = updateImageReferences(${JSON.stringify(html)});
+ const html = await updateImageReferences(${JSON.stringify(html)});
export const frontmatter = ${JSON.stringify(frontmatter)};
export const file = ${JSON.stringify(fileId)};