summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/many-dancers-fold.md22
-rw-r--r--packages/astro/src/assets/utils/emitAsset.ts18
-rw-r--r--packages/astro/src/content/runtime-assets.ts49
-rw-r--r--packages/astro/src/content/template/types.d.ts26
-rw-r--r--packages/astro/src/content/template/virtual-mod-assets.mjs7
-rw-r--r--packages/astro/src/content/template/virtual-mod.mjs7
-rw-r--r--packages/astro/src/content/utils.ts102
-rw-r--r--packages/astro/src/content/vite-plugin-content-imports.ts7
-rw-r--r--packages/astro/src/content/vite-plugin-content-virtual-mod.ts11
-rw-r--r--packages/astro/test/core-image.test.js19
-rw-r--r--packages/astro/test/fixtures/core-image-base/src/content/config.ts4
-rw-r--r--packages/astro/test/fixtures/core-image-errors/src/content/config.ts4
-rw-r--r--packages/astro/test/fixtures/core-image-ssg/src/content/config.ts4
-rw-r--r--packages/astro/test/fixtures/core-image/src/content/blog/one.md4
-rw-r--r--packages/astro/test/fixtures/core-image/src/content/config.ts9
-rw-r--r--packages/astro/test/fixtures/core-image/src/pages/blog/[...slug].astro10
16 files changed, 150 insertions, 153 deletions
diff --git a/.changeset/many-dancers-fold.md b/.changeset/many-dancers-fold.md
new file mode 100644
index 000000000..d2278950a
--- /dev/null
+++ b/.changeset/many-dancers-fold.md
@@ -0,0 +1,22 @@
+---
+'astro': minor
+---
+
+Move `image()` to come from `schema` instead to fix it not working with refine and inside complex types
+
+**Migration**:
+
+Remove the `image` import from `astro:content`, and instead use a function to generate your schema, like such:
+
+```ts
+import { defineCollection, z } from "astro:content";
+
+defineCollection({
+ schema: ({ image }) =>
+ z.object({
+ image: image().refine((img) => img.width >= 200, {
+ message: "image too small",
+ }),
+ }),
+});
+```
diff --git a/packages/astro/src/assets/utils/emitAsset.ts b/packages/astro/src/assets/utils/emitAsset.ts
index 420264289..79775b96d 100644
--- a/packages/astro/src/assets/utils/emitAsset.ts
+++ b/packages/astro/src/assets/utils/emitAsset.ts
@@ -3,19 +3,23 @@ import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import slash from 'slash';
import type { AstroConfig, AstroSettings } from '../../@types/astro';
-import { imageMetadata } from './metadata.js';
+import { imageMetadata, type Metadata } from './metadata.js';
export async function emitESMImage(
- id: string,
+ id: string | undefined,
watchMode: boolean,
fileEmitter: any,
settings: Pick<AstroSettings, 'config'>
-) {
+): Promise<Metadata | undefined> {
+ if (!id) {
+ return undefined;
+ }
+
const url = pathToFileURL(id);
const meta = await imageMetadata(url);
if (!meta) {
- return;
+ return undefined;
}
// Build
@@ -48,13 +52,13 @@ export async function emitESMImage(
* due to Vite dependencies in core.
*/
-function rootRelativePath(config: Pick<AstroConfig, 'root'>, url: URL) {
+function rootRelativePath(config: Pick<AstroConfig, 'root'>, url: URL): string {
const basePath = fileURLToNormalizedPath(url);
const rootPath = fileURLToNormalizedPath(config.root);
return prependForwardSlash(basePath.slice(rootPath.length));
}
-function prependForwardSlash(filePath: string) {
+function prependForwardSlash(filePath: string): string {
return filePath[0] === '/' ? filePath : '/' + filePath;
}
@@ -64,6 +68,6 @@ function fileURLToNormalizedPath(filePath: URL): string {
return slash(fileURLToPath(filePath) + filePath.search).replace(/\\/g, '/');
}
-export function emoji(char: string, fallback: string) {
+export function emoji(char: string, fallback: string): string {
return process.platform !== 'win32' ? char : fallback;
}
diff --git a/packages/astro/src/content/runtime-assets.ts b/packages/astro/src/content/runtime-assets.ts
index 99a83d143..4d07c305b 100644
--- a/packages/astro/src/content/runtime-assets.ts
+++ b/packages/astro/src/content/runtime-assets.ts
@@ -1,20 +1,24 @@
-import { pathToFileURL } from 'url';
+import type { PluginContext } from 'rollup';
import { z } from 'zod';
-import {
- imageMetadata as internalGetImageMetadata,
- type Metadata,
-} from '../assets/utils/metadata.js';
-
-export function createImage(options: { assetsDir: string; relAssetsDir: string }) {
+import type { AstroSettings } from '../@types/astro.js';
+import { emitESMImage } from '../assets/index.js';
+
+export function createImage(
+ settings: AstroSettings,
+ pluginContext: PluginContext,
+ entryFilePath: string
+) {
return () => {
- if (options.assetsDir === 'undefined') {
- throw new Error('Enable `experimental.assets` in your Astro config to use image()');
- }
-
- return z.string({ description: '__image' }).transform(async (imagePath, ctx) => {
- const imageMetadata = await getImageMetadata(pathToFileURL(imagePath));
-
- if (!imageMetadata) {
+ return z.string().transform(async (imagePath, ctx) => {
+ const resolvedFilePath = (await pluginContext.resolve(imagePath, entryFilePath))?.id;
+ const metadata = await emitESMImage(
+ resolvedFilePath,
+ pluginContext.meta.watchMode,
+ pluginContext.emitFile,
+ settings
+ );
+
+ if (!metadata) {
ctx.addIssue({
code: 'custom',
message: `Image ${imagePath} does not exist. Is the path correct?`,
@@ -24,20 +28,7 @@ export function createImage(options: { assetsDir: string; relAssetsDir: string }
return z.NEVER;
}
- return imageMetadata;
+ return metadata;
});
};
}
-
-async function getImageMetadata(
- imagePath: URL
-): Promise<(Metadata & { __astro_asset: true }) | undefined> {
- const meta = await internalGetImageMetadata(imagePath);
-
- if (!meta) {
- return undefined;
- }
-
- delete meta.orientation;
- return { ...meta, __astro_asset: true };
-}
diff --git a/packages/astro/src/content/template/types.d.ts b/packages/astro/src/content/template/types.d.ts
index 2485e2699..346d3ff95 100644
--- a/packages/astro/src/content/template/types.d.ts
+++ b/packages/astro/src/content/template/types.d.ts
@@ -13,8 +13,27 @@ declare module 'astro:content' {
export type CollectionEntry<C extends keyof typeof entryMap> =
(typeof entryMap)[C][keyof (typeof entryMap)[C]];
+ // TODO: Remove this when having this fallback is no longer relevant. 2.3? 3.0? - erika, 2023-04-04
+ /**
+ * @deprecated
+ * `astro:content` no longer provide `image()`.
+ *
+ * Please use it through `schema`, like such:
+ * ```ts
+ * import { defineCollection, z } from "astro:content";
+ *
+ * defineCollection({
+ * schema: ({ image }) =>
+ * z.object({
+ * image: image(),
+ * }),
+ * });
+ * ```
+ */
+ export const image: never;
+
// This needs to be in sync with ImageMetadata
- export const image: () => import('astro/zod').ZodObject<{
+ type ImageFunction = () => import('astro/zod').ZodObject<{
src: import('astro/zod').ZodString;
width: import('astro/zod').ZodNumber;
height: import('astro/zod').ZodNumber;
@@ -45,7 +64,7 @@ declare module 'astro:content' {
| import('astro/zod').ZodEffects<BaseSchemaWithoutEffects>;
type BaseCollectionConfig<S extends BaseSchema> = {
- schema?: S;
+ schema?: S | (({ image }: { image: ImageFunction }) => S);
slug?: (entry: {
id: CollectionEntry<keyof typeof entryMap>['id'];
defaultSlug: string;
@@ -81,8 +100,9 @@ declare module 'astro:content' {
filter?: (entry: CollectionEntry<C>) => unknown
): Promise<CollectionEntry<C>[]>;
+ type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
type InferEntrySchema<C extends keyof typeof entryMap> = import('astro/zod').infer<
- Required<ContentConfig['collections'][C]>['schema']
+ ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
>;
const entryMap: {
diff --git a/packages/astro/src/content/template/virtual-mod-assets.mjs b/packages/astro/src/content/template/virtual-mod-assets.mjs
deleted file mode 100644
index 5f2a1d54c..000000000
--- a/packages/astro/src/content/template/virtual-mod-assets.mjs
+++ /dev/null
@@ -1,7 +0,0 @@
-import { createImage } from 'astro/content/runtime-assets';
-
-const assetsDir = '@@ASSETS_DIR@@';
-
-export const image = createImage({
- assetsDir,
-});
diff --git a/packages/astro/src/content/template/virtual-mod.mjs b/packages/astro/src/content/template/virtual-mod.mjs
index c5dc1b4f3..5e04ac5e7 100644
--- a/packages/astro/src/content/template/virtual-mod.mjs
+++ b/packages/astro/src/content/template/virtual-mod.mjs
@@ -11,6 +11,13 @@ export function defineCollection(config) {
return config;
}
+// TODO: Remove this when having this fallback is no longer relevant. 2.3? 3.0? - erika, 2023-04-04
+export const image = () => {
+ throw new Error(
+ 'Importing `image()` from `astro:content` is no longer supported. See https://docs.astro.build/en/guides/assets/#update-content-collections-schemas for our new import instructions.'
+ );
+};
+
const contentDir = '@@CONTENT_DIR@@';
const entryGlob = import.meta.glob('@@ENTRY_GLOB_PATH@@', {
diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts
index e9c45c5bb..d6e92cd68 100644
--- a/packages/astro/src/content/utils.ts
+++ b/packages/astro/src/content/utils.ts
@@ -3,14 +3,14 @@ import matter from 'gray-matter';
import fsMod from 'node:fs';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
-import type { EmitFile, PluginContext } from 'rollup';
-import { normalizePath, type ErrorPayload as ViteErrorPayload, type ViteDevServer } from 'vite';
+import type { PluginContext } from 'rollup';
+import { normalizePath, type ViteDevServer, type ErrorPayload as ViteErrorPayload } from 'vite';
import { z } from 'zod';
import type { AstroConfig, AstroSettings } from '../@types/astro.js';
-import { emitESMImage } from '../assets/utils/emitAsset.js';
import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { CONTENT_TYPES_FILE } from './consts.js';
import { errorMap } from './error-map.js';
+import { createImage } from './runtime-assets.js';
export const collectionConfigParser = z.object({
schema: z.any().optional(),
@@ -33,7 +33,6 @@ export type CollectionConfig = z.infer<typeof collectionConfigParser>;
export type ContentConfig = z.infer<typeof contentConfigParser>;
type EntryInternal = { rawData: string | undefined; filePath: string };
-
export type EntryInfo = {
id: string;
slug: string;
@@ -45,31 +44,6 @@ export const msg = {
`${collection} does not have a config. We suggest adding one for type safety!`,
};
-/**
- * Mutate (arf) the entryData to reroute assets to their final paths
- */
-export async function patchAssets(
- frontmatterEntry: Record<string, any>,
- watchMode: boolean,
- fileEmitter: EmitFile,
- astroSettings: AstroSettings
-) {
- for (const key of Object.keys(frontmatterEntry)) {
- if (typeof frontmatterEntry[key] === 'object' && frontmatterEntry[key] !== null) {
- if (frontmatterEntry[key]['__astro_asset']) {
- frontmatterEntry[key] = await emitESMImage(
- frontmatterEntry[key].src,
- watchMode,
- fileEmitter,
- astroSettings
- );
- } else {
- await patchAssets(frontmatterEntry[key], watchMode, fileEmitter, astroSettings);
- }
- }
- }
-}
-
export function getEntrySlug({
id,
collection,
@@ -89,71 +63,37 @@ export function getEntrySlug({
export async function getEntryData(
entry: EntryInfo & { unvalidatedData: Record<string, unknown>; _internal: EntryInternal },
collectionConfig: CollectionConfig,
- resolver: (idToResolve: string) => ReturnType<PluginContext['resolve']>
+ pluginContext: PluginContext,
+ settings: AstroSettings
) {
// Remove reserved `slug` field before parsing data
let { slug, ...data } = entry.unvalidatedData;
- if (collectionConfig.schema) {
- // TODO: remove for 2.0 stable release
- if (
- typeof collectionConfig.schema === 'object' &&
- !('safeParseAsync' in collectionConfig.schema)
- ) {
- throw new AstroError({
- title: 'Invalid content collection config',
- message: `New: Content collection schemas must be Zod objects. Update your collection config to use \`schema: z.object({...})\` instead of \`schema: {...}\`.`,
- hint: 'See https://docs.astro.build/en/reference/api-reference/#definecollection for an example.',
- code: 99999,
- });
+
+ let schema = collectionConfig.schema;
+ if (typeof schema === 'function') {
+ if (!settings.config.experimental.assets) {
+ throw new Error(
+ 'The function shape for schema can only be used when `experimental.assets` is enabled.'
+ );
}
+
+ schema = schema({
+ image: createImage(settings, pluginContext, entry._internal.filePath),
+ });
+ }
+
+ if (schema) {
// Catch reserved `slug` field inside schema
// Note: will not warn for `z.union` or `z.intersection` schemas
- if (
- typeof collectionConfig.schema === 'object' &&
- 'shape' in collectionConfig.schema &&
- collectionConfig.schema.shape.slug
- ) {
+ if (typeof schema === 'object' && 'shape' in schema && schema.shape.slug) {
throw new AstroError({
...AstroErrorData.ContentSchemaContainsSlugError,
message: AstroErrorData.ContentSchemaContainsSlugError.message(entry.collection),
});
}
- /**
- * Resolve all the images referred to in the frontmatter from the file requesting them
- */
- async function preprocessAssetPaths(object: Record<string, any>) {
- if (typeof object !== 'object' || object === null) return;
-
- for (let [schemaName, schema] of Object.entries<any>(object)) {
- if (schema._def.description === '__image') {
- object[schemaName] = z.preprocess(
- async (value: unknown) => {
- if (!value || typeof value !== 'string') return value;
- return (
- (await resolver(value))?.id ??
- path.join(path.dirname(entry._internal.filePath), value)
- );
- },
- schema,
- { description: '__image' }
- );
- } else if ('shape' in schema) {
- await preprocessAssetPaths(schema.shape);
- } else if ('unwrap' in schema) {
- const unwrapped = schema.unwrap().shape;
-
- if (unwrapped) {
- await preprocessAssetPaths(unwrapped);
- }
- }
- }
- }
-
- await preprocessAssetPaths(collectionConfig.schema.shape);
-
// Use `safeParseAsync` to allow async transforms
- const parsed = await collectionConfig.schema.safeParseAsync(entry.unvalidatedData, {
+ const parsed = await schema.safeParseAsync(entry.unvalidatedData, {
errorMap,
});
if (parsed.success) {
diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts
index 4437f4fa0..cd944731f 100644
--- a/packages/astro/src/content/vite-plugin-content-imports.ts
+++ b/packages/astro/src/content/vite-plugin-content-imports.ts
@@ -10,6 +10,7 @@ import { AstroError } from '../core/errors/errors.js';
import { escapeViteEnvReferences, getFileInfo } from '../vite-plugin-utils/index.js';
import { CONTENT_FLAG } from './consts.js';
import {
+ NoCollectionError,
getContentEntryExts,
getContentPaths,
getEntryData,
@@ -17,8 +18,6 @@ import {
getEntrySlug,
getEntryType,
globalContentConfigObserver,
- NoCollectionError,
- patchAssets,
type ContentConfig,
} from './utils.js';
@@ -235,11 +234,11 @@ export const _internal = {
? await getEntryData(
{ id, collection, slug, _internal, unvalidatedData },
collectionConfig,
- (idToResolve: string) => pluginContext.resolve(idToResolve, fileId)
+ pluginContext,
+ settings
)
: unvalidatedData;
- await patchAssets(data, pluginContext.meta.watchMode, pluginContext.emitFile, settings);
const contentEntryModule: ContentEntryModule = {
id,
slug,
diff --git a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts
index faa6cb9be..3a72bf1de 100644
--- a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts
+++ b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts
@@ -24,9 +24,6 @@ export function astroContentVirtualModPlugin({
);
const contentEntryExts = getContentEntryExts(settings);
- const assetsDir = settings.config.experimental.assets
- ? contentPaths.assetsDir.toString()
- : 'undefined';
const extGlob =
contentEntryExts.length === 1
? // Wrapping {...} breaks when there is only one extension
@@ -38,14 +35,8 @@ export function astroContentVirtualModPlugin({
.replace('@@CONTENT_DIR@@', relContentDir)
.replace('@@ENTRY_GLOB_PATH@@', entryGlob)
.replace('@@RENDER_ENTRY_GLOB_PATH@@', entryGlob);
- const virtualAssetsModContents = fsMod
- .readFileSync(contentPaths.virtualAssetsModTemplate, 'utf-8')
- .replace('@@ASSETS_DIR@@', assetsDir);
const astroContentVirtualModuleId = '\0' + VIRTUAL_MODULE_ID;
- const allContents = settings.config.experimental.assets
- ? virtualModContents + virtualAssetsModContents
- : virtualModContents;
return {
name: 'astro-content-virtual-mod-plugin',
@@ -58,7 +49,7 @@ export function astroContentVirtualModPlugin({
load(id) {
if (id === astroContentVirtualModuleId) {
return {
- code: allContents,
+ code: virtualModContents,
};
}
},
diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js
index 4b444708e..49f6ce6ae 100644
--- a/packages/astro/test/core-image.test.js
+++ b/packages/astro/test/core-image.test.js
@@ -263,7 +263,7 @@ describe('astro:image', () => {
it('Adds the <img> tags', () => {
let $img = $('img');
- expect($img).to.have.a.lengthOf(4);
+ expect($img).to.have.a.lengthOf(7);
});
it('has proper source for directly used image', () => {
@@ -271,6 +271,19 @@ describe('astro:image', () => {
expect($img.attr('src').startsWith('/src/')).to.equal(true);
});
+ it('has proper source for refined image', () => {
+ let $img = $('#refined-image img');
+ expect($img.attr('src').startsWith('/src/')).to.equal(true);
+ });
+
+ it('has proper sources for array of images', () => {
+ let $img = $('#array-of-images img');
+ const imgsSrcs = [];
+ $img.each((i, img) => imgsSrcs.push(img.attribs['src']));
+ expect($img).to.have.a.lengthOf(2);
+ expect(imgsSrcs.every((img) => img.startsWith('/src/'))).to.be.true;
+ });
+
it('has proper attributes for optimized image through getImage', () => {
let $img = $('#optimized-image-get-image img');
expect($img.attr('src').startsWith('/_image')).to.equal(true);
@@ -365,7 +378,7 @@ describe('astro:image', () => {
it('properly error image in Markdown frontmatter is not found', async () => {
logs.length = 0;
let res = await fixture.fetch('/blog/one');
- const text = await res.text();
+ await res.text();
expect(logs).to.have.a.lengthOf(1);
expect(logs[0].message).to.contain('does not exist. Is the path correct?');
@@ -374,7 +387,7 @@ describe('astro:image', () => {
it('properly error image in Markdown content is not found', async () => {
logs.length = 0;
let res = await fixture.fetch('/post');
- const text = await res.text();
+ await res.text();
expect(logs).to.have.a.lengthOf(1);
expect(logs[0].message).to.contain('Could not find requested image');
diff --git a/packages/astro/test/fixtures/core-image-base/src/content/config.ts b/packages/astro/test/fixtures/core-image-base/src/content/config.ts
index b38ad070e..aa35fb4b6 100644
--- a/packages/astro/test/fixtures/core-image-base/src/content/config.ts
+++ b/packages/astro/test/fixtures/core-image-base/src/content/config.ts
@@ -1,7 +1,7 @@
-import { defineCollection, image, z } from "astro:content";
+import { defineCollection, z } from "astro:content";
const blogCollection = defineCollection({
- schema: z.object({
+ schema: ({image}) => z.object({
title: z.string(),
image: image(),
cover: z.object({
diff --git a/packages/astro/test/fixtures/core-image-errors/src/content/config.ts b/packages/astro/test/fixtures/core-image-errors/src/content/config.ts
index b38ad070e..aa35fb4b6 100644
--- a/packages/astro/test/fixtures/core-image-errors/src/content/config.ts
+++ b/packages/astro/test/fixtures/core-image-errors/src/content/config.ts
@@ -1,7 +1,7 @@
-import { defineCollection, image, z } from "astro:content";
+import { defineCollection, z } from "astro:content";
const blogCollection = defineCollection({
- schema: z.object({
+ schema: ({image}) => z.object({
title: z.string(),
image: image(),
cover: z.object({
diff --git a/packages/astro/test/fixtures/core-image-ssg/src/content/config.ts b/packages/astro/test/fixtures/core-image-ssg/src/content/config.ts
index b38ad070e..aa35fb4b6 100644
--- a/packages/astro/test/fixtures/core-image-ssg/src/content/config.ts
+++ b/packages/astro/test/fixtures/core-image-ssg/src/content/config.ts
@@ -1,7 +1,7 @@
-import { defineCollection, image, z } from "astro:content";
+import { defineCollection, z } from "astro:content";
const blogCollection = defineCollection({
- schema: z.object({
+ schema: ({image}) => z.object({
title: z.string(),
image: image(),
cover: z.object({
diff --git a/packages/astro/test/fixtures/core-image/src/content/blog/one.md b/packages/astro/test/fixtures/core-image/src/content/blog/one.md
index 59a5b77ba..ef0993f63 100644
--- a/packages/astro/test/fixtures/core-image/src/content/blog/one.md
+++ b/packages/astro/test/fixtures/core-image/src/content/blog/one.md
@@ -3,6 +3,10 @@ title: One
image: ~/assets/penguin2.jpg
cover:
image: ../../assets/penguin1.jpg
+arrayOfImages:
+ - ~/assets/penguin2.jpg
+ - ~/assets/penguin1.jpg
+refinedImage: ../../assets/penguin1.jpg
---
# A post
diff --git a/packages/astro/test/fixtures/core-image/src/content/config.ts b/packages/astro/test/fixtures/core-image/src/content/config.ts
index b38ad070e..2d88c49ee 100644
--- a/packages/astro/test/fixtures/core-image/src/content/config.ts
+++ b/packages/astro/test/fixtures/core-image/src/content/config.ts
@@ -1,15 +1,18 @@
-import { defineCollection, image, z } from "astro:content";
+import { defineCollection, z } from "astro:content";
const blogCollection = defineCollection({
- schema: z.object({
+ schema: ({image}) => z.object({
title: z.string(),
image: image(),
cover: z.object({
image: image()
- })
+ }),
+ arrayOfImages: z.array(image()),
+ refinedImage: image().refine((img) => img.width > 200)
}),
});
+
export const collections = {
blog: blogCollection
};
diff --git a/packages/astro/test/fixtures/core-image/src/pages/blog/[...slug].astro b/packages/astro/test/fixtures/core-image/src/pages/blog/[...slug].astro
index b2ccdaeee..33f96a70d 100644
--- a/packages/astro/test/fixtures/core-image/src/pages/blog/[...slug].astro
+++ b/packages/astro/test/fixtures/core-image/src/pages/blog/[...slug].astro
@@ -28,6 +28,16 @@ const myImage = await getImage({src: entry.data.image});
<img src={entry.data.cover.image.src} width={entry.data.cover.image.width} height={entry.data.cover.image.height} />
</div>
+ <div id="array-of-images">
+ {
+ entry.data.arrayOfImages.map((image) => <img src={image.src} />)
+ }
+ </div>
+
+ <div id="refined-image">
+ <img src={entry.data.refinedImage.src} />
+ </div>
+
<div id="optimized-image-get-image">
<img src={myImage.src} {...myImage.attributes} />
</div>