From 26c84efab0dd3773710adde4a801e69aa422490a Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 22 Mar 2023 15:19:37 -0400 Subject: [PATCH] Revert "Support dynamic routes for social images and icons (#47372)" This reverts commit bbd79ac9977e03010e09ff90d933f2ec614b031f. --- package.json | 2 +- .../webpack/loaders/metadata/discover.ts | 48 +++------- .../build/webpack/loaders/next-app-loader.ts | 4 - .../loaders/next-metadata-route-loader.ts | 39 ++------ .../webpack/plugins/middleware-plugin.ts | 2 +- .../src/lib/metadata/is-metadata-route.ts | 19 ++-- .../next/src/lib/metadata/resolve-metadata.ts | 15 ++- pnpm-lock.yaml | 52 +++++------ .../app/apple-icon.tsx | 24 ----- .../app/dynamic/[size]/opengraph-image.tsx | 34 ------- .../app/dynamic/[size]/page.tsx | 3 - .../metadata-dynamic-routes/app/icon.tsx | 25 ----- .../app/opengraph-image.tsx | 23 ----- .../metadata-dynamic-routes/app/page.tsx | 1 - .../app/twitter-image.tsx | 25 ----- .../metadata-dynamic-routes/index.test.ts | 92 +------------------ 16 files changed, 57 insertions(+), 351 deletions(-) delete mode 100644 test/e2e/app-dir/metadata-dynamic-routes/app/apple-icon.tsx delete mode 100644 test/e2e/app-dir/metadata-dynamic-routes/app/dynamic/[size]/opengraph-image.tsx delete mode 100644 test/e2e/app-dir/metadata-dynamic-routes/app/dynamic/[size]/page.tsx delete mode 100644 test/e2e/app-dir/metadata-dynamic-routes/app/icon.tsx delete mode 100644 test/e2e/app-dir/metadata-dynamic-routes/app/opengraph-image.tsx delete mode 100644 test/e2e/app-dir/metadata-dynamic-routes/app/twitter-image.tsx diff --git a/package.json b/package.json index f915d6d2fed25..e958e56ad6cd9 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "@typescript-eslint/eslint-plugin": "4.29.1", "@typescript-eslint/parser": "4.29.1", "@vercel/fetch": "6.1.1", - "@vercel/og": "0.4.0", + "@vercel/og": "0.0.20", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/floating-point-hex-parser": "1.11.1", "@webassemblyjs/helper-api-error": "1.11.1", diff --git a/packages/next/src/build/webpack/loaders/metadata/discover.ts b/packages/next/src/build/webpack/loaders/metadata/discover.ts index 375d55f8add0b..3081ded78e92c 100644 --- a/packages/next/src/build/webpack/loaders/metadata/discover.ts +++ b/packages/next/src/build/webpack/loaders/metadata/discover.ts @@ -80,13 +80,11 @@ export async function createStaticMetadataFromRoute( resolvePath, isRootLayer, loaderContext, - pageExtensions, }: { route: string resolvePath: (pathname: string) => Promise isRootLayer: boolean loaderContext: webpack.LoaderContext - pageExtensions: string[] } ) { let hasStaticMetadataFiles = false @@ -108,46 +106,22 @@ export async function createStaticMetadataFromRoute( const resolvedMetadataFiles = await enumMetadataFiles( resolvedDir, STATIC_METADATA_IMAGES[type].filename, - pageExtensions.concat(STATIC_METADATA_IMAGES[type].extensions), + STATIC_METADATA_IMAGES[type].extensions, opts ) resolvedMetadataFiles .sort((a, b) => a.localeCompare(b)) .forEach((filepath) => { - const [filename, ext] = path.basename(filepath).split('.') - const isDynamicResource = pageExtensions.includes(ext) - - // imageModule type: () => Promise - const imageModule = isDynamicResource - ? `(async () => { - let { alt, size, contentType } = await import(/* webpackMode: "lazy" */ ${JSON.stringify( - filepath - )}) - - const props = { - alt, - type: contentType, - url: ${JSON.stringify(route + '/' + filename)}, - } - if (size) { - ${ - type === 'twitter' || type === 'opengraph' - ? 'props.width = size.width; props.height = size.height;' - : 'props.sizes = size.width + "x" + size.height;' - } - } - return props - })` - : `() => import(/* webpackMode: "eager" */ ${JSON.stringify( - `next-metadata-image-loader?${stringify({ - route, - numericSizes: - type === 'twitter' || type === 'opengraph' ? '1' : undefined, - type, - })}!` + - filepath + - METADATA_RESOURCE_QUERY - )})` + const imageModule = `() => import(/* webpackMode: "eager" */ ${JSON.stringify( + `next-metadata-image-loader?${stringify({ + route, + numericSizes: + type === 'twitter' || type === 'opengraph' ? '1' : undefined, + type, + })}!` + + filepath + + METADATA_RESOURCE_QUERY + )})` hasStaticMetadataFiles = true if (type === 'favicon') { diff --git a/packages/next/src/build/webpack/loaders/next-app-loader.ts b/packages/next/src/build/webpack/loaders/next-app-loader.ts index 223d49000d332..b32bdf0c599b4 100644 --- a/packages/next/src/build/webpack/loaders/next-app-loader.ts +++ b/packages/next/src/build/webpack/loaders/next-app-loader.ts @@ -111,7 +111,6 @@ async function createTreeCodeFromPath( resolvePath, resolveParallelSegments, loaderContext, - pageExtensions, }: { resolver: ( pathname: string, @@ -122,7 +121,6 @@ async function createTreeCodeFromPath( pathname: string ) => [key: string, segment: string | string[]][] loaderContext: webpack.LoaderContext - pageExtensions: string[] } ) { const splittedPath = pagePath.split(/[\\/]/) @@ -163,7 +161,6 @@ async function createTreeCodeFromPath( resolvePath, isRootLayer, loaderContext, - pageExtensions, }) } } catch (err: any) { @@ -382,7 +379,6 @@ const nextAppLoader: AppLoader = async function nextAppLoader() { resolvePath: (pathname: string) => resolve(this.rootContext, pathname), resolveParallelSegments, loaderContext: this, - pageExtensions, }) if (!rootLayout) { diff --git a/packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts b/packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts index a78e39bd23a35..6b3797e8e2b13 100644 --- a/packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts +++ b/packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts @@ -42,6 +42,7 @@ const filePath = fileURLToPath(resourceUrl).replace(${JSON.stringify( )}, '') const buffer = fs.readFileSync(filePath) + export function GET() { return new NextResponse(buffer, { headers: { @@ -55,7 +56,7 @@ export const dynamic = 'force-static' ` } -function getDynamicTextRouteCode(resourcePath: string) { +function getDynamicRouteCode(resourcePath: string) { return `\ import { NextResponse } from 'next/server' import handler from ${JSON.stringify(resourcePath)} @@ -78,19 +79,6 @@ export async function GET() { ` } -function getDynamicImageRouteCode(resourcePath: string) { - return `\ -import { NextResponse } from 'next/server' -import handler from ${JSON.stringify(resourcePath)} - -export async function GET(req, ctx) { - const res = await handler({ params: ctx.params }) - res.headers.set('Cache-Control', 'public, max-age=0, must-revalidate') - return res -} -` -} - // `import.meta.url` is the resource name of the current module. // When it's static route, it could be favicon.ico, sitemap.xml, robots.txt etc. // TODO-METADATA: improve the cache control strategy @@ -99,23 +87,12 @@ const nextMetadataRouterLoader: webpack.LoaderDefinitionFunction r.test(appDirRelativePath)) } -/* - * Remove the 'app' prefix or '/route' suffix, only check the route name since they're only allowed in root app directory - * e.g. - * /app/robots -> /robots - * app/robots -> /robots - * /robots -> /robots - */ export function isMetadataRoute(route: string): boolean { - let page = route.replace(/^\/?app\//, '').replace(/\/route$/, '') - if (page[0] !== '/') page = '/' + page + // Remove the 'app' prefix or '/route' suffix, only check the route name since they're only allowed in root app directory + const page = route.replace(/^\/?app/, '').replace(/\/route$/, '') return ( !page.endsWith('/page') && diff --git a/packages/next/src/lib/metadata/resolve-metadata.ts b/packages/next/src/lib/metadata/resolve-metadata.ts index 0ad2d6bcee07f..d369bf6fc82f1 100644 --- a/packages/next/src/lib/metadata/resolve-metadata.ts +++ b/packages/next/src/lib/metadata/resolve-metadata.ts @@ -45,13 +45,9 @@ function mergeStaticMetadata( if (!staticFilesMetadata) return const { icon, apple, opengraph, twitter } = staticFilesMetadata if (icon || apple) { - // if (!metadata.icons) - metadata.icons = { - icon: icon || [], - apple: apple || [], - } - // if (icon) metadata.icons.icon.push(...icon) - // if (apple) metadata.icons.apple.push(...apple) + if (!metadata.icons) metadata.icons = { icon: [], apple: [] } + if (icon) metadata.icons.icon.push(...icon) + if (apple) metadata.icons.apple.push(...apple) } if (twitter) { const resolvedTwitter = resolveTwitter( @@ -219,8 +215,9 @@ async function collectStaticImagesFiles( if (!metadata?.[type]) return undefined const iconPromises = metadata[type as 'icon' | 'apple'].map( - async (iconResolver: () => Promise) => - interopDefault(await iconResolver()) + // TODO-APP: share the typing between next-metadata-image-loader and here + async (iconResolver: any) => + interopDefault(await iconResolver()) as MetadataImageModule ) return iconPromises?.length > 0 ? await Promise.all(iconPromises) : undefined } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 57be1a8553e04..f6eab37e04e64 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,7 +55,7 @@ importers: '@typescript-eslint/eslint-plugin': 4.29.1 '@typescript-eslint/parser': 4.29.1 '@vercel/fetch': 6.1.1 - '@vercel/og': 0.4.0 + '@vercel/og': 0.0.20 '@webassemblyjs/ast': 1.11.1 '@webassemblyjs/floating-point-hex-parser': 1.11.1 '@webassemblyjs/helper-api-error': 1.11.1 @@ -232,7 +232,7 @@ importers: '@typescript-eslint/eslint-plugin': 4.29.1_qxyn66xcaddhgaahwkbomftvi4 '@typescript-eslint/parser': 4.29.1_6x3mpmmsttbpxxsctsorxedanu '@vercel/fetch': 6.1.1_fii5qhbaymjqmfm7e2spxc5z4m - '@vercel/og': 0.4.0 + '@vercel/og': 0.0.20 '@webassemblyjs/ast': 1.11.1 '@webassemblyjs/floating-point-hex-parser': 1.11.1 '@webassemblyjs/helper-api-error': 1.11.1 @@ -6308,8 +6308,8 @@ packages: resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} dev: true - /@resvg/resvg-wasm/2.4.1: - resolution: {integrity: sha512-yi6R0HyHtsoWTRA06Col4WoDs7SvlXU3DLMNP2bdAgs7HK18dTEVl1weXgxRzi8gwLteGUbIg29zulxIB3GSdg==} + /@resvg/resvg-wasm/2.0.0-alpha.4: + resolution: {integrity: sha512-pWIG9a/x1ky8gXKRhPH1OPKpHFoMN1ISLbJ+O+gPXQHIAKhNd5I28RlWf7q576hAOQA9JZTlo3p/M2uyLzJmmw==} engines: {node: '>= 10'} dev: true @@ -7494,6 +7494,10 @@ packages: '@types/yargs-parser': 13.1.0 dev: true + /@types/yoga-layout/1.9.2: + resolution: {integrity: sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==} + dev: true + /@typescript-eslint/eslint-plugin/4.29.1_qxyn66xcaddhgaahwkbomftvi4: resolution: {integrity: sha512-AHqIU+SqZZgBEiWOrtN94ldR3ZUABV5dUG94j8Nms9rQnHFc8fvDOue/58K4CFz6r8OtDDc35Pw9NQPWo0Ayrw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -7731,13 +7735,13 @@ packages: - supports-color dev: true - /@vercel/og/0.4.0: - resolution: {integrity: sha512-FFjXYCD5ZM0VotC9niy11qf4m6D99JVQ73P/uCLJl/r2PeOj0l26xwTEQ4HHJ/yZ6ncByCNoWAjk+S0fjMJmwg==} + /@vercel/og/0.0.20: + resolution: {integrity: sha512-089P+TfqWz0xBxjOvOhkZIDDtfrLcye94H4IZ+SqxoGPWpNGXaBvRJER/z5SoJxJRcCAL8tPiK5zdjRskM6tLw==} engines: {node: '>=16'} dependencies: - '@resvg/resvg-wasm': 2.4.1 - satori: 0.4.1 - yoga-wasm-web: 0.3.0 + '@resvg/resvg-wasm': 2.0.0-alpha.4 + satori: 0.0.43 + yoga-wasm-web: 0.1.2 dev: true /@webassemblyjs/ast/1.11.1: @@ -8830,11 +8834,6 @@ packages: mixin-deep: 1.3.2 pascalcase: 0.1.1 - /base64-js/0.0.8: - resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==} - engines: {node: '>= 0.4'} - dev: true - /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true @@ -16409,13 +16408,6 @@ packages: resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} dev: true - /linebreak/1.1.0: - resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} - dependencies: - base64-js: 0.0.8 - unicode-trie: 2.0.0 - dev: true - /lines-and-columns/1.1.6: resolution: {integrity: sha512-8ZmlJFVK9iCmtLz19HpSsR8HaAMWBT284VMNednLwlIMDP2hJDCIhUp0IZ2xUcZ+Ob6BM0VvCSJwzASDM45NLQ==} @@ -22277,8 +22269,8 @@ packages: immutable: 4.1.0 source-map-js: 1.0.2 - /satori/0.4.1: - resolution: {integrity: sha512-uQm4+vQj57E9PO/ASAlxsRb+78fHkRUmkNLBBmfuEeGNLGqJ9ufYU7TzYP4XH60PLHYt9Wz1jSsgcAe2cz7+ng==} + /satori/0.0.43: + resolution: {integrity: sha512-SzYwr+LsELWRJU9KMviEOE9TdShry+R5AdS54YQvgAVKFDN4yniAIzwQk1/z2TtIx0ceUT9zTeosWAoWvJBEtQ==} engines: {node: '>=16'} dependencies: '@shuding/opentype.js': 1.4.0-beta.0 @@ -22286,9 +22278,8 @@ packages: css-box-shadow: 1.0.0-3 css-to-react-native: 3.0.0 emoji-regex: 10.2.1 - linebreak: 1.1.0 postcss-value-parser: 4.2.0 - yoga-wasm-web: 0.3.3 + yoga-layout-prebuilt: 1.10.0 dev: true /sax/1.2.4: @@ -25401,12 +25392,15 @@ packages: engines: {node: '>=12.20'} dev: true - /yoga-wasm-web/0.3.0: - resolution: {integrity: sha512-rD3L4jyMlO1m+RWU60lNwZQK5zmzglCV5fI1gTRikmpv3YzmNIZQbjyfE6cMNb9Xaly/C1SwemYGbsiOekMvnQ==} + /yoga-layout-prebuilt/1.10.0: + resolution: {integrity: sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g==} + engines: {node: '>=8'} + dependencies: + '@types/yoga-layout': 1.9.2 dev: true - /yoga-wasm-web/0.3.3: - resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==} + /yoga-wasm-web/0.1.2: + resolution: {integrity: sha512-8SkgawHcA0RUbMrnhxbaQkZDBi8rMed8pQHixkFF9w32zGhAwZ9/cOHWlpYfr6RCx42Yp3siV45/jPEkJxsk6w==} dev: true /zod/3.21.4: diff --git a/test/e2e/app-dir/metadata-dynamic-routes/app/apple-icon.tsx b/test/e2e/app-dir/metadata-dynamic-routes/app/apple-icon.tsx deleted file mode 100644 index 683dd12fcc6f1..0000000000000 --- a/test/e2e/app-dir/metadata-dynamic-routes/app/apple-icon.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ImageResponse } from '@vercel/og' - -export const contentType = 'image/png' - -export default function appleIcon() { - return new ImageResponse( - ( -
- Apple Icon -
- ) - ) -} diff --git a/test/e2e/app-dir/metadata-dynamic-routes/app/dynamic/[size]/opengraph-image.tsx b/test/e2e/app-dir/metadata-dynamic-routes/app/dynamic/[size]/opengraph-image.tsx deleted file mode 100644 index 7b5859a8293ff..0000000000000 --- a/test/e2e/app-dir/metadata-dynamic-routes/app/dynamic/[size]/opengraph-image.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { ImageResponse } from '@vercel/og' - -export const alt = 'Open Graph' - -export default function og({ params }) { - const big = params.size === 'big' - const background = big ? 'orange' : '#000' - return new ImageResponse( - ( -
-
-
- ), - { - width: big === true ? 1200 : 600, - height: big === true ? 630 : 315, - } - ) -} diff --git a/test/e2e/app-dir/metadata-dynamic-routes/app/dynamic/[size]/page.tsx b/test/e2e/app-dir/metadata-dynamic-routes/app/dynamic/[size]/page.tsx deleted file mode 100644 index e1b5bd8f2ce11..0000000000000 --- a/test/e2e/app-dir/metadata-dynamic-routes/app/dynamic/[size]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function page() { - return <>dynamic -} diff --git a/test/e2e/app-dir/metadata-dynamic-routes/app/icon.tsx b/test/e2e/app-dir/metadata-dynamic-routes/app/icon.tsx deleted file mode 100644 index 1205089f21319..0000000000000 --- a/test/e2e/app-dir/metadata-dynamic-routes/app/icon.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ImageResponse } from '@vercel/og' - -export const contentType = 'image/png' -export const size = { width: 512, height: 512 } - -export default function icon() { - return new ImageResponse( - ( -
- Icon -
- ) - ) -} diff --git a/test/e2e/app-dir/metadata-dynamic-routes/app/opengraph-image.tsx b/test/e2e/app-dir/metadata-dynamic-routes/app/opengraph-image.tsx deleted file mode 100644 index fd72c819260e1..0000000000000 --- a/test/e2e/app-dir/metadata-dynamic-routes/app/opengraph-image.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ImageResponse } from '@vercel/og' - -export const alt = 'Open Graph' - -export default function og() { - return new ImageResponse( - ( -
- Open Graph -
- ) - ) -} diff --git a/test/e2e/app-dir/metadata-dynamic-routes/app/page.tsx b/test/e2e/app-dir/metadata-dynamic-routes/app/page.tsx index c2dd96ac06cea..eba737535cc7e 100644 --- a/test/e2e/app-dir/metadata-dynamic-routes/app/page.tsx +++ b/test/e2e/app-dir/metadata-dynamic-routes/app/page.tsx @@ -5,6 +5,5 @@ export default function Page() { } export const metadata = { - metadataBase: new URL('https://deploy-preview-abc.vercel.app'), title: 'index page', } diff --git a/test/e2e/app-dir/metadata-dynamic-routes/app/twitter-image.tsx b/test/e2e/app-dir/metadata-dynamic-routes/app/twitter-image.tsx deleted file mode 100644 index 0190d601348c9..0000000000000 --- a/test/e2e/app-dir/metadata-dynamic-routes/app/twitter-image.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ImageResponse } from '@vercel/og' - -export const alt = 'Twitter' -export const size = { width: 1600, height: 900 } - -export default function twitter() { - return new ImageResponse( - ( -
- Twitter Image -
- ), - size - ) -} 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 644151bddd234..4b49a9e9e8467 100644 --- a/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts +++ b/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts @@ -1,17 +1,13 @@ import { createNextDescribe } from 'e2e-utils' -import imageSize from 'image-size' createNextDescribe( 'app dir - metadata dynamic routes', { files: __dirname, skipDeployment: true, - dependencies: { - '@vercel/og': '0.4.0', - }, }, ({ next }) => { - describe('text routes', () => { + describe('dynamic routes', () => { it('should handle robots.[ext] dynamic routes', async () => { const res = await next.fetch('/robots.txt') const text = await res.text() @@ -60,9 +56,7 @@ createNextDescribe( " `) }) - }) - describe('social image routes', () => { it('should handle manifest.[ext] dynamic routes', async () => { const res = await next.fetch('/manifest.webmanifest') const json = await res.json() @@ -91,90 +85,6 @@ createNextDescribe( ], }) }) - - it('should render og image with opengraph-image dynamic routes', async () => { - const res = await next.fetch('/opengraph-image') - - expect(res.headers.get('content-type')).toBe('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' - ) - }) - - it('should render og image with twitter-image dynamic routes', async () => { - const res = await next.fetch('/twitter-image') - - expect(res.headers.get('content-type')).toBe('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' - ) - }) - - it('should support params as argument in dynamic routes', async () => { - const bufferBig = await ( - await next.fetch('/dynamic/big/opengraph-image') - ).buffer() - const bufferSmall = await ( - await next.fetch('/dynamic/small/opengraph-image') - ).buffer() - - const sizeBig = imageSize(bufferBig) - const sizeSmall = imageSize(bufferSmall) - expect([sizeBig.width, sizeBig.height]).toEqual([1200, 630]) - expect([sizeSmall.width, sizeSmall.height]).toEqual([600, 315]) - }) - }) - - describe('icon image routes', () => { - it('should render icon with dynamic routes', async () => { - const res = await next.fetch('/icon') - - expect(res.headers.get('content-type')).toBe('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' - ) - }) - - it('should render apple icon with dynamic routes', async () => { - const res = await next.fetch('/apple-icon') - - expect(res.headers.get('content-type')).toBe('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' - ) - }) - }) - - it('should inject dynamic metadata properly to head', async () => { - const $ = await next.render$('/') - const $icon = $('link[rel="icon"]') - const $appleIcon = $('link[rel="apple-touch-icon"]') - const ogImageUrl = $('meta[property="og:image"]').attr('content') - const twitterImageUrl = $('meta[name="twitter:image"]').attr('content') - - // non absolute urls - expect($icon.attr('href')).toBe('/icon') - expect($icon.attr('sizes')).toBe('512x512') - expect($icon.attr('type')).toBe('image/png') - expect($appleIcon.attr('href')).toBe('/apple-icon') - expect($appleIcon.attr('sizes')).toBe(undefined) - expect($appleIcon.attr('type')).toBe('image/png') - - // absolute urls - expect(ogImageUrl).toBe( - 'https://deploy-preview-abc.vercel.app/opengraph-image' - ) - expect(twitterImageUrl).toBe( - 'https://deploy-preview-abc.vercel.app/twitter-image' - ) - - // alt text - expect($('meta[property="og:image:alt"]').attr('content')).toBe( - 'Open Graph' - ) - expect($('meta[name="twitter:image:alt"]').attr('content')).toBe( - 'Twitter' - ) }) } )