codecov / codecov-javascript-bundler-plugins

Set of bundler plugins that allow users to upload bundle stats to Codecov.
MIT License
5 stars 1 forks source link

Support non-bundler setups #123

Open AbhiPrasad opened 5 months ago

AbhiPrasad commented 5 months ago

We rely on the bundlers to generate stats for us to upload, but this means we have no current solution for bundlerless setups.

What we can do is to wrap rollup ourselves in a separate package and export a CLI tool that will take some input files, run it through rollup, and send the bundle stats to Sentry. This will mean that tree-shaking also gets automatically applied, which is important to keep in mind.

Usage:

// package.json
{
  "scripts": {
    // supplying package.json is optional, it'll attempt to search for one
    "codecov-bundle-analyze": "@codecov/bundle-analyze --package=./package.json"
  },
}

Under the hood @codecov/bundle-analyze first will look for package.json (if it's not already set) and check if it's defined in it's main, module, and exports fields. See the module docs for more info about those fields. These become the main bundles that we analyze.

For each bundle, we call a small rollup script that takes in the entrypoint and outputs stats. To maintain accuracy we enable preserveModules and don't do any polyfilling or replacements, which means we basically just use rollup to treeshake and walk the dependency tree (and get stats as a side effect hoho).

Let's look at an example, the dotenv package: https://github.com/motdotla/dotenv.

https://github.com/motdotla/dotenv/blob/f6e87eb03705e4b8abcc159ef88e4c0f2992ced4/package.json#L5-L20

Based on the package.json

{
  "main": "lib/main.js",
  "types": "lib/main.d.ts",
  "exports": {
    ".": {
      "types": "./lib/main.d.ts",
      "require": "./lib/main.js",
      "default": "./lib/main.js"
    },
    "./config": "./config.js",
    "./config.js": "./config.js",
    "./lib/env-options": "./lib/env-options.js",
    "./lib/env-options.js": "./lib/env-options.js",
    "./lib/cli-options": "./lib/cli-options.js",
    "./lib/cli-options.js": "./lib/cli-options.js",
    "./package.json": "./package.json"
  },
}

We'd get the following bundles: lib_main.js, config_js, lib_env-options.js, lob-cli-options.js.

edge cases:

AbhiPrasad commented 5 months ago

I did an experiment for this here: https://github.com/codecov/codecov-javascript-bundler-plugins/pull/71

What I found was that introducing a bundler to a project without one might not be the best approach here. What we probably want to do is the following:

  1. For browser projects with a super simple bundling/minification process, we just expose a CLI that reads all files in a output directory (ignoring sourcemaps) and collect sizes for it. This is great for folks who are just using gulp, hardcoding scripts for a wordpress app or using https://github.com/vercel/ncc.

  2. For node/server-side projects we can have to stick with the approach I did in codecov/codecov-javascript-bundler-plugins#71, or you can also just report sizes for everything and put it all in one big chunk/asset. The main issue here is that there is not much of a bundle...

nicholas-codecov commented 5 months ago

I really really like this idea, and glad someone mentioned it 😂

This is where I wish we had an esbuild integration to bring down the bundle times like crazy for insanely large apps, or for people who don't have a bundle step. I wonder if it would be worth looking into this in the future once we have a working support for esbuild.

nwalters512 commented 1 month ago

A separate CLI would work OK, but it's be really nice if y'all could have bundler-plugin-core export a simple async uploadBundleStats(...) function or the like so that those of use without bundlers (or with custom ones) can use to integrate with whatever they're doing. I generally prefer interacting with a typed function directly from JS instead of a CLI.