Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tree Shake] Next.js emit unused code into the main bundle #36702

Closed
1 task done
balazsorban44 opened this issue May 5, 2022 Discussed in #35907 · 1 comment
Closed
1 task done

[Tree Shake] Next.js emit unused code into the main bundle #36702

balazsorban44 opened this issue May 5, 2022 Discussed in #35907 · 1 comment

Comments

@balazsorban44
Copy link
Member

balazsorban44 commented May 5, 2022

Originally posted by @SukkaW April 5, 2022 at #35900

Verify canary release

  • I verified that the issue exists in Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 21.4.0: Mon Feb 21 20:34:37 PST 2022; root:xnu-8020.101.4~2/RELEASE_X86_64
Binaries:
  Node: 16.14.0
  npm: 8.5.1
  Yarn: 1.22.18
  pnpm: 6.32.4
Relevant packages:
  next: 12.1.5-canary.1
  react: 18.0.0
  react-dom: 18.0.0

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

next build / export

Describe the Bug

I notice the main bundle emitted by next build is a bit heavy, so I use @next/bundle-analyzer to analyze my bundle:

image

And there are many features I don't use at all that are included in the bundle:

next/dist/shared/amp.js

I don't use the AMP feature at all. So I take a look at the source code. shared/lib/amp.ts has 2 named export, useAmp and isInAmpMode:

export function isInAmpMode({
ampFirst = false,
hybrid = false,
hasQuery = false,
} = {}): boolean {
return ampFirst || (hybrid && hasQuery)
}
export function useAmp(): boolean {
// Don't assign the context value to a variable to save bytes
return isInAmpMode(React.useContext(AmpStateContext))
}

isInAmpMode is then imported by shared/lib/head.tsx:

import { isInAmpMode } from './amp'

I do use next/head component, so it is very reasonable that isInAmpMode is included in my bundle. However, useAmp is also included in the final bundle:

image

next/dist/client/script.js

I don't use next/script component at all. And the scriptLoader property of the __NEXT_DATA__ is also an empty array:

image

So I take a look at the source code again. client/script.tsx has a default export and a named export initScriptLoader:

export function initScriptLoader(scriptLoaderItems: ScriptProps[]) {
scriptLoaderItems.forEach(handleClientScriptLoad)
}

...which is then imported by client/index.tsx:

if (initialData.scriptLoader) {
const { initScriptLoader } = require('./script')
initScriptLoader(initialData.scriptLoader)
}

However, the entire next/dist/client/script.js is still included in the final main bundle:

image

As you can see there are keywords like text/partydown, afterInteractive and lazyOnLoad.

next/dist/client/streaming/vitals.js

I do use reportWebVitals named export on my custom _app, but I don't use unstable_useWebVitalsReport. So once again, I take a look at the source code of next.js.

There are tons of named export at client/streaming/vitals.ts:

export const webVitalsCallbacks = new Set<ReportWebVitalsCallback>()

export function getBufferedVitalsMetrics() {
return metrics
}

export function trackWebVitalMetric(metric: NextWebVitalsMetric) {
metrics.push(metric)
webVitalsCallbacks.forEach((callback) => callback(metric))
}

export function useWebVitalsReport(callback: ReportWebVitalsCallback) {

flushBufferedVitalsMetrics and trackWebVitalMetric are imported by client/index.tsx:

import {
flushBufferedVitalsMetrics,
trackWebVitalMetric,
} from './streaming/vitals'

... which is used by <Root /> component:

React.useEffect(() => {
measureWebVitals(onPerfEntry)
flushBufferedVitalsMetrics()
}, [])

... and hydrate function:

trackWebVitalMetric(webVitals)

However, useWebVitalsReport is also included in my final bundle:

image

next/dist/client/streaming/refresh.js

I am pretty sure I don't use useRefreshRoot at all. And Next.js itself doesn't uses useRefreshRoot either. However, it is still included in the final bundle:

image

Expected Behavior

All those unused codes I mentioned above should not be included in the final main bundle.

To Reproduce

I introduce @next/bundle-analyzer into another bare bone Next.js reproduction (the one I used in #35286), and all those unused codes are included in the main bundle:

image

@balazsorban44 balazsorban44 added bug Issue was opened via the bug report template. kind: bug and removed bug Issue was opened via the bug report template. labels May 5, 2022
kodiakhq bot pushed a commit that referenced this issue Jun 2, 2022
- Related to #35900 
- Related to #36702 
- Related to #35907
@styfle
Copy link
Member

styfle commented Jun 2, 2022

Thanks for reporting @SukkaW!

The amp bug has been fixed in #37383.

The others are still being investigated. If you have a solution, please submit a PR and we'll take a look, thanks!

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants