From 06206402f06fb857ef4b6290cbaae6d4162e6e5f Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Mon, 6 May 2024 23:59:11 +0200 Subject: [PATCH] Generate OG images as WebP (#7940) --- package.json | 4 +- pnpm-lock.yaml | 96 ++++++++++++++++--------------- src/pages/open-graph/[...path].ts | 9 +++ src/util/getOgImageUrl.ts | 2 +- 4 files changed, 63 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 16b095b1d91ff..6242220c5ca0d 100644 --- a/package.json +++ b/package.json @@ -41,9 +41,9 @@ "astro": "^4.6.3", "astro-auto-import": "^0.4.2", "astro-eslint-parser": "^0.16.0", - "astro-og-canvas": "^0.4.1", + "astro-og-canvas": "^0.5.0", "bcp-47-normalize": "^2.1.0", - "canvaskit-wasm": "^0.37.0", + "canvaskit-wasm": "^0.39.1", "dedent-js": "^1.0.1", "domhandler": "^4.3.1", "eslint": "^8.29.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 325d53be5d7d3..5047828076ec2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ dependencies: version: 0.21.1(astro@4.6.3) '@docsearch/js': specifier: ^3.5.2 - version: 3.5.2(@algolia/client-search@4.23.2)(search-insights@2.13.0) + version: 3.5.2(@algolia/client-search@4.23.3)(search-insights@2.13.0) '@fontsource/ibm-plex-mono': specifier: ^4.5.10 version: 4.5.10 @@ -92,14 +92,14 @@ devDependencies: specifier: ^0.16.0 version: 0.16.0 astro-og-canvas: - specifier: ^0.4.1 - version: 0.4.1(astro@4.6.3) + specifier: ^0.5.0 + version: 0.5.0(astro@4.6.3) bcp-47-normalize: specifier: ^2.1.0 version: 2.1.0 canvaskit-wasm: - specifier: ^0.37.0 - version: 0.37.0 + specifier: ^0.39.1 + version: 0.39.1 dedent-js: specifier: ^1.0.1 version: 1.0.1 @@ -233,47 +233,47 @@ packages: tunnel: 0.0.6 dev: true - /@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0)(search-insights@2.13.0): + /@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0)(search-insights@2.13.0): resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==} dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0)(search-insights@2.13.0) - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0) + '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0)(search-insights@2.13.0) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights dev: false - /@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0)(search-insights@2.13.0): + /@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0)(search-insights@2.13.0): resolution: {integrity: sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==} peerDependencies: search-insights: '>= 1 < 3' dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0) search-insights: 2.13.0 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch dev: false - /@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0): + /@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0): resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==} peerDependencies: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0) - '@algolia/client-search': 4.23.2 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0) + '@algolia/client-search': 4.23.3 algoliasearch: 4.20.0 dev: false - /@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0): + /@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0): resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==} peerDependencies: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' dependencies: - '@algolia/client-search': 4.23.2 + '@algolia/client-search': 4.23.3 algoliasearch: 4.20.0 dev: false @@ -287,8 +287,8 @@ packages: resolution: {integrity: sha512-vCfxauaZutL3NImzB2G9LjLt36vKAckc6DhMp05An14kVo8F1Yofb6SIl6U3SaEz8pG2QOB9ptwM5c+zGevwIQ==} dev: false - /@algolia/cache-common@4.23.2: - resolution: {integrity: sha512-OUK/6mqr6CQWxzl/QY0/mwhlGvS6fMtvEPyn/7AHUx96NjqDA4X4+Ju7aXFQKh+m3jW9VPB0B9xvEQgyAnRPNw==} + /@algolia/cache-common@4.23.3: + resolution: {integrity: sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==} dev: false /@algolia/cache-in-memory@4.20.0: @@ -321,11 +321,11 @@ packages: '@algolia/transporter': 4.20.0 dev: false - /@algolia/client-common@4.23.2: - resolution: {integrity: sha512-Q2K1FRJBern8kIfZ0EqPvUr3V29ICxCm/q42zInV+VJRjldAD9oTsMGwqUQ26GFMdFYmqkEfCbY4VGAiQhh22g==} + /@algolia/client-common@4.23.3: + resolution: {integrity: sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==} dependencies: - '@algolia/requester-common': 4.23.2 - '@algolia/transporter': 4.23.2 + '@algolia/requester-common': 4.23.3 + '@algolia/transporter': 4.23.3 dev: false /@algolia/client-personalization@4.20.0: @@ -344,20 +344,20 @@ packages: '@algolia/transporter': 4.20.0 dev: false - /@algolia/client-search@4.23.2: - resolution: {integrity: sha512-CxSB29OVGSE7l/iyoHvamMonzq7Ev8lnk/OkzleODZ1iBcCs3JC/XgTIKzN/4RSTrJ9QybsnlrN/bYCGufo7qw==} + /@algolia/client-search@4.23.3: + resolution: {integrity: sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==} dependencies: - '@algolia/client-common': 4.23.2 - '@algolia/requester-common': 4.23.2 - '@algolia/transporter': 4.23.2 + '@algolia/client-common': 4.23.3 + '@algolia/requester-common': 4.23.3 + '@algolia/transporter': 4.23.3 dev: false /@algolia/logger-common@4.20.0: resolution: {integrity: sha512-xouigCMB5WJYEwvoWW5XDv7Z9f0A8VoXJc3VKwlHJw/je+3p2RcDXfksLI4G4lIVncFUYMZx30tP/rsdlvvzHQ==} dev: false - /@algolia/logger-common@4.23.2: - resolution: {integrity: sha512-jGM49Q7626cXZ7qRAWXn0jDlzvoA1FvN4rKTi1g0hxKsTTSReyYk0i1ADWjChDPl3Q+nSDhJuosM2bBUAay7xw==} + /@algolia/logger-common@4.23.3: + resolution: {integrity: sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==} dev: false /@algolia/logger-console@4.20.0: @@ -376,8 +376,8 @@ packages: resolution: {integrity: sha512-9h6ye6RY/BkfmeJp7Z8gyyeMrmmWsMOCRBXQDs4mZKKsyVlfIVICpcSibbeYcuUdurLhIlrOUkH3rQEgZzonng==} dev: false - /@algolia/requester-common@4.23.2: - resolution: {integrity: sha512-3EfpBS0Hri0lGDB5H/BocLt7Vkop0bTTLVUBB844HH6tVycwShmsV6bDR7yXbQvFP1uNpgePRD3cdBCjeHmk6Q==} + /@algolia/requester-common@4.23.3: + resolution: {integrity: sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==} dev: false /@algolia/requester-node-http@4.20.0: @@ -394,12 +394,12 @@ packages: '@algolia/requester-common': 4.20.0 dev: false - /@algolia/transporter@4.23.2: - resolution: {integrity: sha512-GY3aGKBy+8AK4vZh8sfkatDciDVKad5rTY2S10Aefyjh7e7UGBP4zigf42qVXwU8VOPwi7l/L7OACGMOFcjB0Q==} + /@algolia/transporter@4.23.3: + resolution: {integrity: sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==} dependencies: - '@algolia/cache-common': 4.23.2 - '@algolia/logger-common': 4.23.2 - '@algolia/requester-common': 4.23.2 + '@algolia/cache-common': 4.23.3 + '@algolia/logger-common': 4.23.3 + '@algolia/requester-common': 4.23.3 dev: false /@ampproject/remapping@2.2.1: @@ -970,10 +970,10 @@ packages: /@docsearch/css@3.5.2: resolution: {integrity: sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==} - /@docsearch/js@3.5.2(@algolia/client-search@4.23.2)(search-insights@2.13.0): + /@docsearch/js@3.5.2(@algolia/client-search@4.23.3)(search-insights@2.13.0): resolution: {integrity: sha512-p1YFTCDflk8ieHgFJYfmyHBki1D61+U9idwrLh+GQQMrBSP3DLGKpy0XUJtPjAOPltcVbqsTjiPFfH7JImjUNg==} dependencies: - '@docsearch/react': 3.5.2(@algolia/client-search@4.23.2)(search-insights@2.13.0) + '@docsearch/react': 3.5.2(@algolia/client-search@4.23.3)(search-insights@2.13.0) preact: 10.16.0 transitivePeerDependencies: - '@algolia/client-search' @@ -983,7 +983,7 @@ packages: - search-insights dev: false - /@docsearch/react@3.5.2(@algolia/client-search@4.23.2)(search-insights@2.13.0): + /@docsearch/react@3.5.2(@algolia/client-search@4.23.3)(search-insights@2.13.0): resolution: {integrity: sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' @@ -1000,8 +1000,8 @@ packages: search-insights: optional: true dependencies: - '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0)(search-insights@2.13.0) - '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.23.2)(algoliasearch@4.20.0) + '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0)(search-insights@2.13.0) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.20.0) '@docsearch/css': 3.5.2 algoliasearch: 4.20.0 search-insights: 2.13.0 @@ -2118,6 +2118,10 @@ packages: resolution: {integrity: sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg==} dev: false + /@webgpu/types@0.1.21: + resolution: {integrity: sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==} + dev: true + /acorn-jsx@5.3.2(acorn@8.11.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2329,14 +2333,14 @@ packages: remark-expressive-code: 0.33.4 dev: false - /astro-og-canvas@0.4.1(astro@4.6.3): - resolution: {integrity: sha512-/o5roIF8IuizF+9L9h6X9wHh17d3O2rQJJsOkNtlYBNZFSIkIG/R12TmGACG9ZdViRr6aLhV9BG+B+XLzC0dmw==} + /astro-og-canvas@0.5.0(astro@4.6.3): + resolution: {integrity: sha512-nrwZpFL9d6tVJRKV5OLZa3xSSh+Cbaaoay/R9jAgHCrJ79WNyN2rgRT/+wtP1O/bCQ+wVP3GC3mBQN7vogImvg==} engines: {node: '>=18.14.1'} peerDependencies: astro: ^3.0.0 || ^4.0.0 dependencies: astro: 4.6.3(@types/node@18.6.4)(sass@1.54.3)(typescript@5.0.2) - canvaskit-wasm: 0.37.0 + canvaskit-wasm: 0.39.1 deterministic-object-hash: 2.0.2 entities: 4.5.0 dev: true @@ -2628,8 +2632,10 @@ packages: resolution: {integrity: sha512-ej+w/m8Jzpv9Z7W7uJZer14Ke8P2ogsjg4ZMGIuq4iqUOqY2Jq8BNW42iGmNfRwREaaEfFIczLuZZiEVSYNHAA==} dev: false - /canvaskit-wasm@0.37.0: - resolution: {integrity: sha512-hegK3gdVQJwgOre02kFHJDEo9b+VHY/bA/N+12C7fECSosGL9Q7gwcz4vnDdOnzSoeLCRH26oTNK3gEO+dm+dg==} + /canvaskit-wasm@0.39.1: + resolution: {integrity: sha512-Gy3lCmhUdKq+8bvDrs9t8+qf7RvcjuQn+we7vTVVyqgOVO1UVfHpsnBxkTZw+R4ApEJ3D5fKySl9TU11hmjl/A==} + dependencies: + '@webgpu/types': 0.1.21 dev: true /catharsis@0.9.0: diff --git a/src/pages/open-graph/[...path].ts b/src/pages/open-graph/[...path].ts index 3a73d96b1d00b..60d52f4a170d8 100644 --- a/src/pages/open-graph/[...path].ts +++ b/src/pages/open-graph/[...path].ts @@ -19,9 +19,18 @@ export const { getStaticPaths, GET } = OGImageRoute({ pages, + getSlug(path) { + path = path.replace(/^\/src\/pages\//, ''); + path = path.replace(/\.[^.]*$/, '') + '.webp'; + path = path.replace(/\/index\.webp$/, '.webp'); + return path; + }, + getImageOptions: async (_, { data, slug }: (typeof pages)[string]): Promise => { const isRtl = rtlLanguages.has(getLangFromSlug(slug)); return { + format: 'WEBP', + quality: 90, title: data.title, description: data.description, dir: isRtl ? 'rtl' : 'ltr', diff --git a/src/util/getOgImageUrl.ts b/src/util/getOgImageUrl.ts index b87ca5e28c9ce..f661a528d4764 100644 --- a/src/util/getOgImageUrl.ts +++ b/src/util/getOgImageUrl.ts @@ -13,7 +13,7 @@ const paths = new Set(routes.map(({ params }) => params.path)); * @returns Path to the OpenGraph image if found. Otherwise, `undefined`. */ export function getOgImageUrl(path: string, isFallback: boolean): string | undefined { - let imagePath = path.replace(/^\//, '').replace(/\/$/, '') + '.png'; + let imagePath = path.replace(/^\//, '').replace(/\/$/, '') + '.webp'; if (isFallback) { // Replace the language segment with 'en' for fallback pages. imagePath = 'en' + imagePath.slice(imagePath.indexOf('/'));