pastelsky / bundlephobia

🏋️ Find out the cost of adding a new frontend dependency to your project
https://bundlephobia.com
MIT License
9.02k stars 223 forks source link

Export Analysis reports wrong sizes #364

Closed wereHamster closed 1 year ago

wereHamster commented 4 years ago

Describe the bug

https://bundlephobia.com/result?p=@timvir/core@0.0.14

The size of the fullWidth and extendedWidth should be a few bytes, not 9kB! If you look at the source (https://unpkg.com/browse/@timvir/core@0.0.14/index.js), the extendedWidth export is essentially this:

function cx(...classNames) {
  return classNames.filter(Boolean).join(' ');
}

const noLayout = "n13r7o41";
export const extendedWidth = cx(noLayout, "ev9kr8h");

To Reproduce

Source and output from bundlephobia available in the links above.

Expected behavior

The size of the fullWidth and extendedWidth exports should be a few bytes. The whole block above is only ~163 bytes (uncompressed).

redbar0n commented 3 years ago

It is also unclear from the webpage if Exports Analysis minifies before it gzip's.

redbar0n commented 3 years ago

This seems to explain the bug: https://github.com/pastelsky/bundlephobia/issues/253#issuecomment-569600466

pastelsky commented 3 years ago

Bundlephobia reports true tree shaken sizes — which might or might not represent the actual size of the module.

If the individual functions have not been structured in a way that allows webpack to tree-shake individual modules, the users will end up importing the entire library (yes, even for two line functions).

To test this, I'd recommend checking bundle sizes by spinning up a fresh copy of create react app, and trying the following —

  1. CJS Import const a = require('@timvir/core') and then
  2. Do a ESM import import { fullWidth } from '@timvir/core'

And see if the final JS bundle sizes differ. This is most probably an issue with your package rather than a bug in bundlephobia.

redbar0n commented 3 years ago

@pastelsky Thanks for clarifying. That makes sense. If so, then bundlephobia should write a disclaimer under Exports Analysis headline explaining that, so people don’t get the wrong impression. Ideally, both the tree shaken size and the individual differential size of the export should be shown. In their respective gzipped over-the-wire size, of course. Because sometime one wants to use several of the modules, which happen to utilise the same base dependencies, and then you’d get the wrong impression of their size by adding together their sizes from their individually tree shaken sizes.

pablo-abc commented 2 years ago

I'm having the same issue with some of my packages. This one being a great example since it's just a bunch of utils. I'm using Rollup to bundle these packages but I have confirmed in a simple project that webpack does tree shake unused imports. This is how I'm testing it:

// webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  mode: 'development',
  optimization: {
    usedExports: true,
    innerGraph: true,
    sideEffects: true,
  },
  devtool: false,
};
// src/index.js
import { _get } from '@felte/common';

const res = _get({ prop: 'value' }, 'prop');
console.log(res);
// package.json
{
  "main": "index.js",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^5.66.0",
    "webpack-cli": "^4.9.1"
  },
  "dependencies": {
    "@felte/common": "0.6.0"
  }
}

The resulting bundle does tree-shake everything but _get. But, unless I'm understanding incorrectly what Bundlephobia's export analysis does, it's implying this would bundle 2.3kb instead of the few bytes that the method itself uses.

Note: these are all named exports, exported from a single index.js file.

pablo-abc commented 2 years ago

Upon further inspection I think I found a solution to this. Removing the "browser" property from my package's package.json, or pointing it to the esm output makes Bundlephobia report the correct sizes now as seen here. The only thing I find weird about this is that package-build-stats correctly pointed the path to my esm output, even if it wasn't using that one to calculate the sizes?

There's basically three things required for Bundlephobia to report the correct sizes of each module on its "Export Analysis":

  1. Your exported modules should each be in a separate file
  2. Your bundler should preserve said modules in separate files (in rollup this would mean either using multiple entry points, or using output.preserveModules = true).
  3. Your package.json should point its "browser" property to your esm bundle, not umd nor cjs.

I'm not sure if this is a bug in Bundlephobia, but webpack does tree shake properly regardless of the "browser" property (as per my previous comment).