Skip to content

Observe bundle size #7018

Description

@PavelVanecek

We want to make sure that we allow tree-shaking, or at least that we don't make the situation actively worse.

We have the bundle reports from Codecov which is something but it reports on the library size, which isn't entirely relevant if the app bundler will tree-shake it all later.

Here is a proof of concept script that I made:

import path from 'node:path';
import fs from 'node:fs';
import { rollup } from 'rollup';
import virtual from '@rollup/plugin-virtual';
import { createRequire, findPackageJSON } from 'node:module';

const require = createRequire(import.meta.url);
const { ProjectDocReader } = require('../omnidoc/readProject') as typeof import('../omnidoc/readProject');

const packageJsonFilePath = findPackageJSON(import.meta.url);

const es6Entry = path.join(path.dirname(packageJsonFilePath), 'es6', 'index.js');

const packageJsonContent = JSON.parse(fs.readFileSync(packageJsonFilePath, 'utf8'));

const dependencies = Object.keys(packageJsonContent.dependencies);
const peerDependencies = Object.keys(packageJsonContent.peerDependencies);
const specialDependencies = [/^victory-vendor.*/, /^es-toolkit.*/, 'use-sync-external-store/shim/with-selector'];

const allDependencies = [...dependencies, ...peerDependencies, ...specialDependencies];

async function treeshake(entry: string | string[], used: boolean = true) {
  const entries = Array.isArray(entry) ? entry : [entry];
  const importList = entries.join(', ');
  const source = used
    ? `import { ${importList} } from '${es6Entry}';\nexport const used = { ${importList} };\n`
    : `import { ${importList} } from '${es6Entry}';\n`;
  const bundle = await rollup({
    input: 'entry',
    external: allDependencies,
    plugins: [
      virtual({
        entry: source,
      }),
    ],
    treeshake: {
      moduleSideEffects: false,
    },
  });

  const output = await bundle.generate({
    format: 'esm',
  });

  return output.output;
}

// usage on all exports
 new ProjectDocReader().getAllRuntimeExportedNames().forEach(name => {
   treeshake(name, false).then(output => {
     const hasCode = output.some(chunkOrAsset => {
       if (chunkOrAsset.type === 'chunk') {
         return chunkOrAsset.code.trim() !== '';
       } else {
         return chunkOrAsset.source.trim() !== '';
       }
     });
     console.log(`${name}: ${hasCode ? 'used' : 'unused'}`);
   });
 });

// usage on single export
 treeshake('Line').then(output => {
   for (const chunkOrAsset of output) {
     if (chunkOrAsset.type === 'chunk') {
       console.log(`Chunk: ${chunkOrAsset.fileName}`);
       console.log(chunkOrAsset.code);
     } else {
       console.log(`Asset: ${chunkOrAsset.fileName}`);
       console.log(chunkOrAsset.source);
     }
   }
 });

Iterate on this script.

What we want to achieve is:

  • Have a script where we can input a list of components, and it will output bundle size, and list of components that are present in the output
  • Use this script to generate several bundles: one for all cartesian components, one for all polar components, and then one bundle for Treemap, Sunburts, Sankey. Report the bundle sizes using scripts/upload-bundle-analysis.js.
  • Add a test suite that runs this script for each component, and flags all components with insufficient tree-shaking. The idea is that importing one component should never pull out more components than asked for. There are some pre-existing problems in the library; we expect this test suite to show some failures.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions