-
Notifications
You must be signed in to change notification settings - Fork 27.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Optimize bundle size for appDir (#42252)
- Ensure React / other deps are deduped against main-app bundle - Only require web-vitals when it's needed - Move warnOnce into separate file as it's not tree shaken - Add create-next-app with bundle analyzer <details> <summary>13.0.0 (react-dom deduping bug)</summary> <img width="1912" alt="13-0-0" src="https://user-images.githubusercontent.com/6324199/199067942-d2394ffa-fc1c-4606-94f4-b489ef959a9b.png"> </details> <details> <summary>Canary (react-dom deduping bug fixed)</summary> <img width="1912" alt="current-canary-branch" src="https://user-images.githubusercontent.com/6324199/199067999-786b523e-b4f0-4044-8d2e-acaa0386771a.png"> </details> <details> <summary>This PR</summary> <img width="1912" alt="pr-changes" src="https://user-images.githubusercontent.com/6324199/199068060-2fdb6e11-8b94-4683-9b8e-65bb6faab34a.png"> </details> ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
- Loading branch information
1 parent
848bb3a
commit 8e3586d
Showing
15 changed files
with
414 additions
and
19 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
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,103 @@ | ||
/* global location */ | ||
import type { Metric, ReportCallback } from 'next/dist/compiled/web-vitals' | ||
|
||
// copied to prevent pulling in un-necessary utils | ||
const WEB_VITALS = ['CLS', 'FCP', 'FID', 'INP', 'LCP', 'TTFB'] | ||
|
||
const initialHref = location.href | ||
let isRegistered = false | ||
let userReportHandler: ReportCallback | undefined | ||
type Attribution = typeof WEB_VITALS[number] | ||
|
||
function onReport(metric: Metric): void { | ||
if (userReportHandler) { | ||
userReportHandler(metric) | ||
} | ||
|
||
// This code is not shipped, executed, or present in the client-side | ||
// JavaScript bundle unless explicitly enabled in your application. | ||
// | ||
// When this feature is enabled, we'll make it very clear by printing a | ||
// message during the build (`next build`). | ||
if ( | ||
process.env.NODE_ENV === 'production' && | ||
// This field is empty unless you explicitly configure it: | ||
process.env.__NEXT_ANALYTICS_ID | ||
) { | ||
const body: Record<string, string> = { | ||
dsn: process.env.__NEXT_ANALYTICS_ID, | ||
id: metric.id, | ||
page: window.__NEXT_DATA__?.page, | ||
href: initialHref, | ||
event_name: metric.name, | ||
value: metric.value.toString(), | ||
speed: | ||
'connection' in navigator && | ||
(navigator as any)['connection'] && | ||
'effectiveType' in (navigator as any)['connection'] | ||
? ((navigator as any)['connection']['effectiveType'] as string) | ||
: '', | ||
} | ||
|
||
const blob = new Blob([new URLSearchParams(body).toString()], { | ||
// This content type is necessary for `sendBeacon`: | ||
type: 'application/x-www-form-urlencoded', | ||
}) | ||
const vitalsUrl = 'https://vitals.vercel-insights.com/v1/vitals' | ||
// Navigator has to be bound to ensure it does not error in some browsers | ||
// https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch | ||
const send = navigator.sendBeacon && navigator.sendBeacon.bind(navigator) | ||
|
||
function fallbackSend() { | ||
fetch(vitalsUrl, { | ||
body: blob, | ||
method: 'POST', | ||
credentials: 'omit', | ||
keepalive: true, | ||
// console.error is used here as when the fetch fails it does not affect functioning of the app | ||
}).catch(console.error) | ||
} | ||
|
||
try { | ||
// If send is undefined it'll throw as well. This reduces output code size. | ||
send!(vitalsUrl, blob) || fallbackSend() | ||
} catch (err) { | ||
fallbackSend() | ||
} | ||
} | ||
} | ||
|
||
export default (onPerfEntry?: ReportCallback): void => { | ||
if (process.env.__NEXT_ANALYTICS_ID) { | ||
// Update function if it changes: | ||
userReportHandler = onPerfEntry | ||
|
||
// Only register listeners once: | ||
if (isRegistered) { | ||
return | ||
} | ||
isRegistered = true | ||
|
||
const attributions: Attribution[] | undefined = process.env | ||
.__NEXT_WEB_VITALS_ATTRIBUTION as any | ||
|
||
for (const webVital of WEB_VITALS) { | ||
try { | ||
let mod: any | ||
|
||
if (process.env.__NEXT_HAS_WEB_VITALS_ATTRIBUTION) { | ||
if (attributions?.includes(webVital)) { | ||
mod = require('next/dist/compiled/web-vitals-attribution') | ||
} | ||
} | ||
if (!mod) { | ||
mod = require('next/dist/compiled/web-vitals') | ||
} | ||
mod[`on${webVital}`](onReport) | ||
} catch (err) { | ||
// Do nothing if the module fails to load | ||
console.warn(`Failed to track ${webVital} web-vital`, err) | ||
} | ||
} | ||
} | ||
} |
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,12 @@ | ||
let warnOnce = (_: string) => {} | ||
if (process.env.NODE_ENV !== 'production') { | ||
const warnings = new Set<string>() | ||
warnOnce = (msg: string) => { | ||
if (!warnings.has(msg)) { | ||
console.warn(msg) | ||
} | ||
warnings.add(msg) | ||
} | ||
} | ||
|
||
export { warnOnce } |
4 changes: 4 additions & 0 deletions
4
test/e2e/app-dir/create-next-app-template/.vscode/settings.json
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,4 @@ | ||
{ | ||
"typescript.tsdk": "./node_modules/typescript/lib", | ||
"typescript.enablePromptUseWorkspaceTsdk": true | ||
} |
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,26 @@ | ||
html, | ||
body { | ||
padding: 0; | ||
margin: 0; | ||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, | ||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; | ||
} | ||
|
||
a { | ||
color: inherit; | ||
text-decoration: none; | ||
} | ||
|
||
* { | ||
box-sizing: border-box; | ||
} | ||
|
||
@media (prefers-color-scheme: dark) { | ||
html { | ||
color-scheme: dark; | ||
} | ||
body { | ||
color: white; | ||
background: black; | ||
} | ||
} |
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,18 @@ | ||
import './globals.css' | ||
|
||
export default function RootLayout({ | ||
children, | ||
}: { | ||
children: React.ReactNode | ||
}) { | ||
return ( | ||
<html lang="en"> | ||
<head> | ||
<title>Create Next App</title> | ||
<meta name="description" content="Generated by create next app" /> | ||
<link rel="icon" href="/favicon.ico" /> | ||
</head> | ||
<body>{children}</body> | ||
</html> | ||
) | ||
} |
146 changes: 146 additions & 0 deletions
146
test/e2e/app-dir/create-next-app-template/app/page.module.css
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,146 @@ | ||
.container { | ||
padding: 0 2rem; | ||
} | ||
|
||
.main { | ||
min-height: 100vh; | ||
padding: 4rem 0; | ||
flex: 1; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
} | ||
|
||
.footer { | ||
display: flex; | ||
flex: 1; | ||
padding: 2rem 0; | ||
border-top: 1px solid #eaeaea; | ||
justify-content: center; | ||
align-items: center; | ||
} | ||
|
||
.footer a { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
flex-grow: 1; | ||
} | ||
|
||
.title { | ||
margin: 0; | ||
line-height: 1.15; | ||
font-size: 4rem; | ||
font-style: normal; | ||
font-weight: 800; | ||
letter-spacing: -0.025em; | ||
} | ||
|
||
.title a { | ||
text-decoration: none; | ||
color: #0070f3; | ||
} | ||
|
||
.title a:hover, | ||
.title a:focus, | ||
.title a:active { | ||
text-decoration: underline; | ||
} | ||
|
||
.title, | ||
.description { | ||
text-align: center; | ||
} | ||
|
||
.description { | ||
margin: 4rem 0; | ||
line-height: 1.5; | ||
font-size: 1.5rem; | ||
} | ||
|
||
.code { | ||
background: #fafafa; | ||
border-radius: 5px; | ||
padding: 0.75rem; | ||
font-size: 1.1rem; | ||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, | ||
Bitstream Vera Sans Mono, Courier New, monospace; | ||
} | ||
|
||
.grid { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
flex-wrap: wrap; | ||
max-width: 1200px; | ||
} | ||
|
||
.card { | ||
margin: 1rem; | ||
padding: 1.5rem; | ||
text-align: left; | ||
color: inherit; | ||
text-decoration: none; | ||
border: 1px solid #eaeaea; | ||
border-radius: 10px; | ||
transition: color 0.15s ease, border-color 0.15s ease; | ||
max-width: 300px; | ||
} | ||
|
||
.card:hover, | ||
.card:focus, | ||
.card:active { | ||
color: #0070f3; | ||
border-color: #0070f3; | ||
} | ||
|
||
.card h2 { | ||
margin: 0 0 1rem 0; | ||
font-size: 1.5rem; | ||
} | ||
|
||
.card p { | ||
margin: 0; | ||
font-size: 1.25rem; | ||
line-height: 1.5; | ||
} | ||
|
||
.logo { | ||
height: 1em; | ||
margin-left: 0.5rem; | ||
} | ||
|
||
@media (max-width: 600px) { | ||
.grid { | ||
width: 100%; | ||
flex-direction: column; | ||
} | ||
} | ||
|
||
@media (prefers-color-scheme: dark) { | ||
.title { | ||
background: linear-gradient(180deg, #ffffff 0%, #aaaaaa 100%); | ||
-webkit-background-clip: text; | ||
-webkit-text-fill-color: transparent; | ||
background-clip: text; | ||
text-fill-color: transparent; | ||
} | ||
.title a { | ||
background: linear-gradient(180deg, #0070f3 0%, #0153af 100%); | ||
-webkit-background-clip: text; | ||
-webkit-text-fill-color: transparent; | ||
background-clip: text; | ||
text-fill-color: transparent; | ||
} | ||
.card, | ||
.footer { | ||
border-color: #222; | ||
} | ||
.code { | ||
background: #111; | ||
} | ||
.logo img { | ||
filter: invert(1); | ||
} | ||
} |
Oops, something went wrong.