From 93ad2f22e97ad2976f584fec615765ce989ae94e Mon Sep 17 00:00:00 2001 From: Archana Agivale Date: Wed, 7 Aug 2024 15:18:26 +0530 Subject: [PATCH] Sitemap image tag support (#68034) Image Tag should be available in sitemap.xml, which is not currently support in sitemap. In this pr, I solved issue #68033 which I faced in my project. cc @leerob @delbaoliveira @samcx x-ref: https://developers.google.com/search/docs/crawling-indexing/sitemaps/image-sitemaps --------- Co-authored-by: Jiachi Liu Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> --- .../01-metadata/sitemap.mdx | 40 +++++++++++++++++++ .../metadata/resolve-route-data.test.ts | 27 +++++++++++++ .../loaders/metadata/resolve-route-data.ts | 9 +++++ .../lib/metadata/types/metadata-interface.ts | 1 + .../app/sitemap-image/sitemap.ts | 20 ++++++++++ .../metadata-dynamic-routes/index.test.ts | 12 ++++++ test/turbopack-build-tests-manifest.json | 1 + 7 files changed, 110 insertions(+) create mode 100644 test/e2e/app-dir/metadata-dynamic-routes/app/sitemap-image/sitemap.ts diff --git a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx index 8a10c89579c09..d361a2bec8afb 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx @@ -120,6 +120,46 @@ Output: ``` +### Image Sitemaps + +You can use `images` property to create image sitemaps. Learn more details in the [Google Developer Docs](https://developers.google.com/search/docs/crawling-indexing/sitemaps/image-sitemaps). + +```ts filename="app/sitemap.ts" switcher +import type { MetadataRoute } from 'next' + +export default function sitemap(): MetadataRoute.Sitemap { + return [ + { + url: 'https://example.com', + lastModified: '2021-01-01', + changeFrequency: 'weekly', + priority: 0.5, + images: ['https://example.com/image.jpg'], + }, + ] +} +``` + +Output: + +```xml filename="acme.com/sitemap.xml" + + + + https://example.com + + https://example.com/image.jpg + + 2021-01-01 + weekly + 0.5 + + +``` + ### Generate a localized Sitemap ```ts filename="app/sitemap.ts" switcher diff --git a/packages/next/src/build/webpack/loaders/metadata/resolve-route-data.test.ts b/packages/next/src/build/webpack/loaders/metadata/resolve-route-data.test.ts index 7c0b22de50c71..82e686afd6707 100644 --- a/packages/next/src/build/webpack/loaders/metadata/resolve-route-data.test.ts +++ b/packages/next/src/build/webpack/loaders/metadata/resolve-route-data.test.ts @@ -126,5 +126,32 @@ describe('resolveRouteData', () => { " `) }) + it('should resolve sitemap.xml with images', () => { + expect( + resolveSitemap([ + { + url: 'https://example.com', + lastModified: '2021-01-01', + changeFrequency: 'weekly', + priority: 0.5, + images: ['https://example.com/image.jpg'], + }, + ]) + ).toMatchInlineSnapshot(` + " + + + https://example.com + + https://example.com/image.jpg + + 2021-01-01 + weekly + 0.5 + + + " + `) + }) }) }) diff --git a/packages/next/src/build/webpack/loaders/metadata/resolve-route-data.ts b/packages/next/src/build/webpack/loaders/metadata/resolve-route-data.ts index 368fdbaebbdb7..4c10b73bb9881 100644 --- a/packages/next/src/build/webpack/loaders/metadata/resolve-route-data.ts +++ b/packages/next/src/build/webpack/loaders/metadata/resolve-route-data.ts @@ -47,10 +47,14 @@ export function resolveSitemap(data: MetadataRoute.Sitemap): string { const hasAlternates = data.some( (item) => Object.keys(item.alternates ?? {}).length > 0 ) + const hasImages = data.some((item) => Boolean(item.images?.length)) let content = '' content += '\n' content += '\n` } } + if (item.images?.length) { + for (const image of item.images) { + content += `\n${image}\n\n` + } + } if (item.lastModified) { const serializedDate = item.lastModified instanceof Date diff --git a/packages/next/src/lib/metadata/types/metadata-interface.ts b/packages/next/src/lib/metadata/types/metadata-interface.ts index a8bd0d9fc426c..6b0aa16cdc4ad 100644 --- a/packages/next/src/lib/metadata/types/metadata-interface.ts +++ b/packages/next/src/lib/metadata/types/metadata-interface.ts @@ -607,6 +607,7 @@ type SitemapFile = Array<{ alternates?: { languages?: Languages } + images?: string[] }> type ResolvingMetadata = Promise diff --git a/test/e2e/app-dir/metadata-dynamic-routes/app/sitemap-image/sitemap.ts b/test/e2e/app-dir/metadata-dynamic-routes/app/sitemap-image/sitemap.ts new file mode 100644 index 0000000000000..5ff6e59228463 --- /dev/null +++ b/test/e2e/app-dir/metadata-dynamic-routes/app/sitemap-image/sitemap.ts @@ -0,0 +1,20 @@ +import { MetadataRoute } from 'next' + +export default function sitemap(): MetadataRoute.Sitemap { + return [ + { + url: 'https://example.com', + lastModified: '2024-01-01', + changeFrequency: 'daily', + priority: 0.5, + }, + { + url: 'https://example.com/about', + lastModified: '2024-01-01', + images: [ + 'https://example.com/image1.jpg', + 'https://example.com/image2.jpg', + ], + }, + ] +} diff --git a/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts b/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts index 2dca3ba618dbc..f1613464ac123 100644 --- a/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts +++ b/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts @@ -103,6 +103,18 @@ describe('app dir - metadata dynamic routes', () => { `` ) }) + + it('should support images in sitemap', async () => { + const xml = await (await next.fetch('/sitemap-image/sitemap.xml')).text() + + expect(xml).toContain( + `\nhttps://example.com/image1.jpg\n` + ) + expect(xml).toContain( + `\nhttps://example.com/image2.jpg\n` + ) + }) + if (isNextStart) { it('should optimize routes without multiple generation API as static routes', async () => { const appPathsManifest = JSON.parse( diff --git a/test/turbopack-build-tests-manifest.json b/test/turbopack-build-tests-manifest.json index 0c6f256dbd155..06d59e33bf405 100644 --- a/test/turbopack-build-tests-manifest.json +++ b/test/turbopack-build-tests-manifest.json @@ -2063,6 +2063,7 @@ "app dir - metadata dynamic routes sitemap should not throw if client components are imported but not used in sitemap", "app dir - metadata dynamic routes sitemap should optimize routes without multiple generation API as static routes", "app dir - metadata dynamic routes sitemap should support alternate.languages in sitemap", + "app dir - metadata dynamic routes sitemap should support images in sitemap", "app dir - metadata dynamic routes sitemap should support generate multi sitemaps with generateSitemaps", "app dir - metadata dynamic routes social image routes should fill params into dynamic routes url of metadata images", "app dir - metadata dynamic routes social image routes should fill params into routes groups url of static images",