Files
next.js/test/production/disable-fallback-polyfills/index.test.ts
Luke SandbergandGitHub 18e7c84af2 Remove JS size reporting from next build (#83815)
## What

Remove the `First Used JS` and `Size` metrics from `next build` CLI output, as well as the manifest files that supported these metrics.

## Why

A recent investigation has revealed a number of bugs and longstanding known issues in the implementation.

* The metrics produced by Webpack builds for App Router don't account for layouts and parallel routes correctly.
   * Not only are they not accounted for in route sizes, they are treated as distinct routes for the 'uniqueness' computation which means dependencies of a widely shared layout wouldn't be treated as 'common chunks'
* The `First Used JS by All` metric counts `.css` chunks as JS
    * This happened to predominately affect turbopack reporting due the above issue in webpack (css deps of layouts don't get treated as common deps)
* In App router, client components are not accurately modeled by either toolchain
   * webpack doesn't account for client components at all.  Leading to consistent undercounts
   * turbopack accumulates all referenced client components which is better but can be an overcount for conditionally rendered or mutually exclusive components.
*  In Pages router, `next/dynamic` dependencies can confuse these metrics

Generally, if a server is using any non-trivial logic to decide what is rendered there is just no correct answer to the 'First Load JS size' question.   These metrics originally came out of a collaboration with Chrome DevRel back in 2019 (maybe? first reference i think is https://github.com/vercel/next.js/pull/7194).  Since then, the web has shifted to Core Web Vitals and indeed that is what we use to evaluate actual performance.

We've decided to remove the metric instead as it's misleading at this point.

See #83823 for docs updates
2025-09-18 23:57:41 -07:00

107 lines
3.1 KiB
TypeScript

import { createNext } from 'e2e-utils'
import type { NextInstance } from 'e2e-utils'
import { statSync } from 'fs'
import { join } from 'path'
// TODO: Implement experimental.fallbackNodePolyfills
;(process.env.IS_TURBOPACK_TEST ? describe.skip : describe)(
'Disable fallback polyfills',
() => {
let next: NextInstance
async function getIndexPageSize() {
// Read build manifest to get chunk files for the index page
// this only works reliably for pages router and simple examples.
const buildManifest = await next.readJSON('.next/build-manifest.json')
// Get chunks for the '/' page
const indexPageChunks = buildManifest.pages['/'] || []
// Calculate total size of all chunks for the index page
let totalSize = 0
for (const chunkPath of indexPageChunks) {
const fullChunkPath = join(next.testDir, '.next', chunkPath)
try {
const stats = statSync(fullChunkPath)
totalSize += stats.size
} catch (error) {
console.warn(`Could not read chunk: ${chunkPath}`, error.message)
}
}
// Convert to kB for easier comparison
return totalSize / 1024
}
beforeAll(async () => {
next = await createNext({
files: {
'pages/index.js': `
import { useEffect } from 'react'
import crypto from 'crypto'
export default function Page() {
useEffect(() => {
crypto;
}, [])
return <p>hello world</p>
}
`,
},
dependencies: {
axios: '0.27.2',
},
})
await next.stop()
})
afterAll(() => next.destroy())
it('Fallback polyfills added by default', async () => {
const indexPageSizeKB = await getIndexPageSize()
console.log(
`Index page size (with polyfills): ${indexPageSizeKB.toFixed(2)} kB`
)
expect(indexPageSizeKB).not.toBeLessThan(400)
})
it('Reduced bundle size when polyfills are disabled', async () => {
await next.patchFile(
'next.config.js',
`module.exports = {
experimental: {
fallbackNodePolyfills: false
}
}`
)
await next.start()
await next.stop()
const indexPageSizeKB = await getIndexPageSize()
console.log(
`Index page size (without polyfills): ${indexPageSizeKB.toFixed(2)} kB`
)
expect(indexPageSizeKB).toBeLessThan(400)
})
it('Pass build without error if non-polyfilled module is unreachable', async () => {
// `axios` uses `Buffer`, but it should be unreachable in the browser.
// https://github.com/axios/axios/blob/649d739288c8e2c55829ac60e2345a0f3439c730/lib/helpers/toFormData.js#L138
await next.patchFile(
'pages/index.js',
`import axios from 'axios'
import { useEffect } from 'react'
export default function Home() {
useEffect(() => {
axios.get('/api')
}, [])
return "hello world"
}`
)
await expect(next.start()).not.toReject()
})
}
)