rsnext/test/e2e/app-dir/metadata-edge/index.test.ts

34 lines
1.1 KiB
TypeScript
Raw Normal View History

Fix tree-shaking for metadata image functions on the Edge runtime (#51762) This PR fixes tree-shaking for the metadata image generation module (e.g. `opengraph-image.js` and other conventions) when the page has `runtime = 'edge'`. ## Details The first step of this fix is to change this from the loader: ```js import * as exported from "./opengraph-image.js" ``` to be necessary fields only (so the `default` export can potentially be removed): ```js import { alt, size } from "./opengraph-image.js" ``` To know which fields are exported, we need to load the module first via Webpack loader's `loadModule` API and check its `HarmonyExportSpecifierDependency` dependencies. This is the first step to make it tree-shakable. Since we have `./opengraph-image.js` used in another entry, the actual image API route `opengraph-image/route.js`: ```js import * as image from "./opengraph-image.js" ``` Webpack still treats both as the same module and generates one chunk for it. We want to "fork" it into two modules. The technique here is to add a noop resource query and make it: ```js import { alt, size } from "./opengraph-image.js?__next_metadata_image_meta__" ``` So it won't be shared in the chunk (as it's a different request), and can be concatenated inline. However that's not enough, the inlined result will still have all imports from our `opengraph-image.js`, including `import { ImageResponse } from 'next/server'`. Because we can't simply add `"sideEffects": false` in Next.js' package.json, we need a way to mark this import as side-effect free. I went through https://github.com/webpack/webpack/blob/main/lib/optimize/SideEffectsFlagPlugin.js and used the same method to mark that module with `module.factoryMeta.sideEffectFree = true`. With all these added, the page bundle will no longer contain the `ImageResponse` instance. ## Result The difference is quite amazing, for the new added test (an empty Edge runtime page with an opengrah image file) here're the before/after metrics for the `page.js` server bundle: Edge bundle size: 892kB → 500kB. Build time: 26.792s → 8.830s.
2023-06-26 13:44:29 +02:00
import { createNextDescribe } from 'e2e-utils'
import imageSize from 'image-size'
createNextDescribe(
'app dir - Metadata API on the Edge runtime',
{
files: __dirname,
},
({ next, isNextStart }) => {
describe('OG image route', () => {
if (isNextStart) {
it('should not bundle `ImageResponse` into the page worker', async () => {
const pageBundle = await next.readFile('.next/server/app/page.js')
Fix tree-shaking for metadata image functions on the Edge runtime (#51762) This PR fixes tree-shaking for the metadata image generation module (e.g. `opengraph-image.js` and other conventions) when the page has `runtime = 'edge'`. ## Details The first step of this fix is to change this from the loader: ```js import * as exported from "./opengraph-image.js" ``` to be necessary fields only (so the `default` export can potentially be removed): ```js import { alt, size } from "./opengraph-image.js" ``` To know which fields are exported, we need to load the module first via Webpack loader's `loadModule` API and check its `HarmonyExportSpecifierDependency` dependencies. This is the first step to make it tree-shakable. Since we have `./opengraph-image.js` used in another entry, the actual image API route `opengraph-image/route.js`: ```js import * as image from "./opengraph-image.js" ``` Webpack still treats both as the same module and generates one chunk for it. We want to "fork" it into two modules. The technique here is to add a noop resource query and make it: ```js import { alt, size } from "./opengraph-image.js?__next_metadata_image_meta__" ``` So it won't be shared in the chunk (as it's a different request), and can be concatenated inline. However that's not enough, the inlined result will still have all imports from our `opengraph-image.js`, including `import { ImageResponse } from 'next/server'`. Because we can't simply add `"sideEffects": false` in Next.js' package.json, we need a way to mark this import as side-effect free. I went through https://github.com/webpack/webpack/blob/main/lib/optimize/SideEffectsFlagPlugin.js and used the same method to mark that module with `module.factoryMeta.sideEffectFree = true`. With all these added, the page bundle will no longer contain the `ImageResponse` instance. ## Result The difference is quite amazing, for the new added test (an empty Edge runtime page with an opengrah image file) here're the before/after metrics for the `page.js` server bundle: Edge bundle size: 892kB → 500kB. Build time: 26.792s → 8.830s.
2023-06-26 13:44:29 +02:00
expect(pageBundle).not.toContain('ImageResponse')
const sharedPageBundle = await next.readFile(
'.next/server/app/another/page.js'
)
expect(sharedPageBundle).not.toContain('ImageResponse')
Fix tree-shaking for metadata image functions on the Edge runtime (#51762) This PR fixes tree-shaking for the metadata image generation module (e.g. `opengraph-image.js` and other conventions) when the page has `runtime = 'edge'`. ## Details The first step of this fix is to change this from the loader: ```js import * as exported from "./opengraph-image.js" ``` to be necessary fields only (so the `default` export can potentially be removed): ```js import { alt, size } from "./opengraph-image.js" ``` To know which fields are exported, we need to load the module first via Webpack loader's `loadModule` API and check its `HarmonyExportSpecifierDependency` dependencies. This is the first step to make it tree-shakable. Since we have `./opengraph-image.js` used in another entry, the actual image API route `opengraph-image/route.js`: ```js import * as image from "./opengraph-image.js" ``` Webpack still treats both as the same module and generates one chunk for it. We want to "fork" it into two modules. The technique here is to add a noop resource query and make it: ```js import { alt, size } from "./opengraph-image.js?__next_metadata_image_meta__" ``` So it won't be shared in the chunk (as it's a different request), and can be concatenated inline. However that's not enough, the inlined result will still have all imports from our `opengraph-image.js`, including `import { ImageResponse } from 'next/server'`. Because we can't simply add `"sideEffects": false` in Next.js' package.json, we need a way to mark this import as side-effect free. I went through https://github.com/webpack/webpack/blob/main/lib/optimize/SideEffectsFlagPlugin.js and used the same method to mark that module with `module.factoryMeta.sideEffectFree = true`. With all these added, the page bundle will no longer contain the `ImageResponse` instance. ## Result The difference is quite amazing, for the new added test (an empty Edge runtime page with an opengrah image file) here're the before/after metrics for the `page.js` server bundle: Edge bundle size: 892kB → 500kB. Build time: 26.792s → 8.830s.
2023-06-26 13:44:29 +02:00
})
}
})
it('should render OpenGraph image meta tag correctly', async () => {
const html$ = await next.render$('/')
const ogUrl = new URL(html$('meta[property="og:image"]').attr('content'))
const imageBuffer = await (await next.fetch(ogUrl.pathname)).buffer()
const size = imageSize(imageBuffer)
expect([size.width, size.height]).toEqual([1200, 630])
})
}
)