From b85c8a78a116dbbddc901438bc0b7a1917dc0238 Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 13 Sep 2023 16:44:15 +0200 Subject: [PATCH] feat: better errors for images (#8536) --- .changeset/slow-mirrors-provide.md | 5 ++++ packages/astro/src/assets/internal.ts | 4 ++-- packages/astro/src/assets/services/service.ts | 15 ++++++++---- packages/astro/src/core/errors/dev/utils.ts | 12 +++++----- packages/astro/src/core/errors/errors-data.ts | 24 +++++++++++++------ packages/astro/src/core/errors/overlay.ts | 4 ++-- 6 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 .changeset/slow-mirrors-provide.md diff --git a/.changeset/slow-mirrors-provide.md b/.changeset/slow-mirrors-provide.md new file mode 100644 index 000000000..768b6106d --- /dev/null +++ b/.changeset/slow-mirrors-provide.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Improved error messages around `astro:assets` diff --git a/packages/astro/src/assets/internal.ts b/packages/astro/src/assets/internal.ts index dd5e427f6..1a266c07a 100644 --- a/packages/astro/src/assets/internal.ts +++ b/packages/astro/src/assets/internal.ts @@ -77,12 +77,12 @@ export async function getImage( const service = await getConfiguredImageService(); - // If the user inlined an import, something fairly common especially in MDX, await it for them + // If the user inlined an import, something fairly common especially in MDX, or passed a function that returns an Image, await it for them const resolvedOptions: ImageTransform = { ...options, src: typeof options.src === 'object' && 'then' in options.src - ? (await options.src).default + ? (await options.src).default ?? (await options.src) : options.src, }; diff --git a/packages/astro/src/assets/services/service.ts b/packages/astro/src/assets/services/service.ts index 5af4a898b..69894aa0c 100644 --- a/packages/astro/src/assets/services/service.ts +++ b/packages/astro/src/assets/services/service.ts @@ -1,6 +1,6 @@ import type { AstroConfig } from '../../@types/astro.js'; import { AstroError, AstroErrorData } from '../../core/errors/index.js'; -import { joinPaths } from '../../core/path.js'; +import { isRemotePath, joinPaths } from '../../core/path.js'; import { VALID_SUPPORTED_FORMATS } from '../consts.js'; import { isESMImportedImage, isRemoteAllowed } from '../internal.js'; import type { ImageOutputFormat, ImageTransform } from '../types.js'; @@ -126,13 +126,20 @@ export const baseService: Omit = { if (!options.src || (typeof options.src !== 'string' && typeof options.src !== 'object')) { throw new AstroError({ ...AstroErrorData.ExpectedImage, - message: AstroErrorData.ExpectedImage.message(JSON.stringify(options.src)), + message: AstroErrorData.ExpectedImage.message( + JSON.stringify(options.src), + typeof options.src, + JSON.stringify(options, (_, v) => (v === undefined ? null : v)) + ), }); } if (!isESMImportedImage(options.src)) { - // User passed an `/@fs/` path instead of the full image. - if (options.src.startsWith('/@fs/')) { + // User passed an `/@fs/` path or a filesystem path instead of the full image. + if ( + options.src.startsWith('/@fs/') || + (!isRemotePath(options.src) && !options.src.startsWith('/')) + ) { throw new AstroError({ ...AstroErrorData.LocalImageUsedWrongly, message: AstroErrorData.LocalImageUsedWrongly.message(options.src), diff --git a/packages/astro/src/core/errors/dev/utils.ts b/packages/astro/src/core/errors/dev/utils.ts index 837b52fde..fd69b74db 100644 --- a/packages/astro/src/core/errors/dev/utils.ts +++ b/packages/astro/src/core/errors/dev/utils.ts @@ -227,21 +227,21 @@ export function getDocsForError(err: ErrorWithMetadata): string | undefined { * Render a subset of Markdown to HTML or a CLI output */ export function renderErrorMarkdown(markdown: string, target: 'html' | 'cli') { - const linkRegex = /\[(.+)\]\((.+)\)/gm; + const linkRegex = /\[([^\[]+)\]\((.*)\)/gm; const boldRegex = /\*\*(.+)\*\*/gm; - const urlRegex = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\\/%?=~_|!:,.;]*[-A-Z0-9+&@#\\/%=~_|])/gim; + const urlRegex = / (\b(https?|ftp):\/\/[-A-Z0-9+&@#\\/%?=~_|!:,.;]*[-A-Z0-9+&@#\\/%=~_|])/gim; const codeRegex = /`([^`]+)`/gim; if (target === 'html') { return escape(markdown) .replace(linkRegex, `$1`) .replace(boldRegex, '$1') - .replace(urlRegex, ' $1 ') + .replace(urlRegex, ' $1') .replace(codeRegex, '$1'); } else { return markdown - .replace(linkRegex, (fullMatch, m1, m2) => `${bold(m1)} ${underline(m2)}`) - .replace(urlRegex, (fullMatch) => ` ${underline(fullMatch.trim())} `) - .replace(boldRegex, (fullMatch, m1) => `${bold(m1)}`); + .replace(linkRegex, (_, m1, m2) => `${bold(m1)} ${underline(m2)}`) + .replace(urlRegex, (fullMatch) => ` ${underline(fullMatch.trim())}`) + .replace(boldRegex, (_, m1) => `${bold(m1)}`); } } diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index d63fc8852..1f336e5f8 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -594,9 +594,9 @@ export const PrerenderDynamicEndpointPathCollide = { export const ExpectedImage = { name: 'ExpectedImage', title: 'Expected src to be an image.', - message: (options: string) => - `Expected \`src\` property to be either an ESM imported image or a string with the path of a remote image. Received \`${options}\`.`, - hint: 'This error can often happen because of a wrong path. Make sure the path to your image is correct.', + message: (src: string, typeofOptions: string, fullOptions: string) => + `Expected \`src\` property for \`getImage\` or \`\` to be either an ESM imported image or a string with the path of a remote image. Received \`${src}\` (type: \`${typeofOptions}\`).\n\nFull serialized options received: \`${fullOptions}\`.`, + hint: "This error can often happen because of a wrong path. Make sure the path to your image is correct. If you're passing an async function, make sure to call and await it.", } satisfies ErrorData; /** * @docs @@ -712,12 +712,15 @@ export const LocalsNotAnObject = { '`locals` can only be assigned to an object. Other values like numbers, strings, etc. are not accepted.', hint: 'If you tried to remove some information from the `locals` object, try to use `delete` or set the property to `undefined`.', } satisfies ErrorData; + /** * @docs * @see * - [Images](https://docs.astro.build/en/guides/images/) * @description - * When using the default image services, `Image`'s and `getImage`'s `src` parameter must be either an imported image or an URL, it cannot be a filepath. + * When using the default image services, `Image`'s and `getImage`'s `src` parameter must be either an imported image or an URL, it cannot be a string of a filepath. + * + * For local images from content collections, you can use the [image() schema helper](https://docs.astro.build/en/guides/images/#images-in-content-collections) to resolve the images. * * ```astro * --- @@ -728,15 +731,22 @@ export const LocalsNotAnObject = { * * Cool image * - * + * + * Cool image + * + * * Cool image + * + * + * Cool image * ``` */ export const LocalImageUsedWrongly = { name: 'LocalImageUsedWrongly', - title: 'ESM imported images must be passed as-is.', + title: 'Local images must be imported.', message: (imageFilePath: string) => - `\`Image\`'s and \`getImage\`'s \`src\` parameter must be an imported image or an URL, it cannot be a filepath. Received \`${imageFilePath}\`.`, + `\`Image\`'s and \`getImage\`'s \`src\` parameter must be an imported image or an URL, it cannot be a string filepath. Received \`${imageFilePath}\`.`, + hint: 'If you want to use an image from your `src` folder, you need to either import it or if the image is coming from a content collection, use the [image() schema helper](https://docs.astro.build/en/guides/images/#images-in-content-collections) See https://docs.astro.build/en/guides/images/#src-required for more information on the `src` property.', } satisfies ErrorData; /** diff --git a/packages/astro/src/core/errors/overlay.ts b/packages/astro/src/core/errors/overlay.ts index 5a24f898a..1ee6fc9d2 100644 --- a/packages/astro/src/core/errors/overlay.ts +++ b/packages/astro/src/core/errors/overlay.ts @@ -336,7 +336,7 @@ const style = /* css */ ` #message-content, #hint-content { white-space: pre-wrap; - line-height: 24px; + line-height: 26px; flex-grow: 1; } @@ -369,7 +369,7 @@ const style = /* css */ ` #message-hints code { font-family: var(--font-monospace); background-color: var(--border); - padding: 4px; + padding: 2px 4px; border-radius: var(--roundiness); white-space: nowrap; }