-
Notifications
You must be signed in to change notification settings - Fork 26.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix tree-shaking for metadata image functions on the Edge runtime (#5…
…1762) 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.
- Loading branch information
Showing
8 changed files
with
149 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
export default function Layout({ children }) { | ||
return ( | ||
<html> | ||
<head></head> | ||
<body>{children}</body> | ||
</html> | ||
) | ||
} | ||
|
||
export const metadata = { | ||
metadataBase: new URL('https://mydomain.com'), | ||
title: 'Next.js App', | ||
description: 'This is a Next.js App', | ||
twitter: { | ||
cardType: 'summary_large_image', | ||
title: 'Twitter - Next.js App', | ||
description: 'Twitter - This is a Next.js App', | ||
}, | ||
alternates: { | ||
canonical: './', | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { ImageResponse } from 'next/server' | ||
|
||
export const alt = 'Open Graph' | ||
|
||
export default function og() { | ||
return new ImageResponse( | ||
( | ||
<div | ||
style={{ | ||
width: '100%', | ||
height: '100%', | ||
display: 'flex', | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
fontSize: 128, | ||
background: 'lavender', | ||
}} | ||
> | ||
Open Graph | ||
</div> | ||
) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react' | ||
|
||
export default function Page() { | ||
return <>hello index</> | ||
} | ||
|
||
export const metadata = { | ||
title: 'index page', | ||
} | ||
|
||
export const runtime = 'edge' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
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/middleware-manifest.json' | ||
) | ||
expect(pageBundle).not.toContain('ImageResponse') | ||
}) | ||
} | ||
}) | ||
|
||
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]) | ||
}) | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module.exports = {} | ||
|
||
// For development: analyze the bundled chunks for stats app | ||
if (process.env.ANALYZE) { | ||
const withBundleAnalyzer = require('@next/bundle-analyzer')({ | ||
enabled: true, | ||
}) | ||
module.exports = withBundleAnalyzer(module.exports) | ||
} |