unjs / unbuild

📦 A unified JavaScript build system
MIT License
2.3k stars 89 forks source link

Support generating build meta #235

Open jrson83 opened 1 year ago

jrson83 commented 1 year ago

Describe the feature

tsup already integrated the --metafile flag to tell esbuild to produce some metadata about the build in JSON format.

It is great for analyzing with bundle-buddy.com. To see support for this in unbuild would be great!

More Info:

Additional information

pi0 commented 1 year ago

I love the idea! Renamed PR title to introduce it as an option (we could enable also via CLI flag).

jrson83 commented 1 year ago

I love the idea! Renamed PR title to introduce it as an option (we could enable also via CLI flag).

Cool. I checked out the source to see if I'm able to implement the feature. I understand that unbuild uses rollup as bundler and esbuild as rollup plugin.

It seems that esbuilds metafile option is only part of the BuildOptions interface, which is used by the esbuild build function:

export let build: typeof types.build = (options: types.BuildOptions) {} // ...

https://esbuild.github.io/api/#build

unbuild does not use the build function, but instead the transform function which uses TransformOptions interface, on which the metafile option is not available.

https://esbuild.github.io/api/#transform

When selecting rollup on bundle-buddy.com, there is an example of how to implement a plugin function on rollups buildEnd hook, which writes a bundle-buddy compatible file:

plugins: [
  {
    buildEnd() {
      const deps = [];
      for (const id of this.getModuleIds()) {
        const m = this.getModuleInfo(id);
        if (m != null && !m.isExternal) {
          for (const target of m.importedIds) {
            deps.push({ source: m.id, target })
          }
        }
      }

      fs.writeFileSync(
          path.join(__dirname, 'graph.json'), 
          JSON.stringify(deps, null, 2));
    },
  }
]

But it additionally requires a sourcemap generated by rollup:

output: { 
  file: '`${outFolder}/dist.js',
  format: 'iife',
  name: 'PROJECT_NAME',
  sourcemap: true,
}

Since I just saw the issue #236, it would be possible to add a metafile option to the RollupBuildOptions interface and create a plugin I just tested:

import { writeFile } from "node:fs/promises";
import type { Plugin } from "rollup";
import { resolve } from "pathe";

export interface MetafileOptions {
  enable?: boolean;
}

export interface MetaInfo {
  source: string;
  target: string;
}

const defaults: MetafileOptions = {
  enable: false,
};

export function metafilePlugin(opts: MetafileOptions = {}): Plugin {
  opts = { ...opts, ...defaults };
  return {
    name: "unbuild-metafile",
    async buildEnd(err) {
      if (!err && opts.enable) {
        const deps: MetaInfo[] = [];

        for (const id of this.getModuleIds()) {
          const m = this.getModuleInfo(id);
          if (m != null && !m.isExternal) {
            for (const target of m.importedIds) {
              deps.push({ source: m.id, target });
            }
          }
        }

        if (Array.isArray(deps) && deps.length === 0) {
          return;
        }

        const outPath = resolve(__dirname || process.cwd(), "graph.json");
        await writeFile(outPath, JSON.stringify(deps), "utf8");
      }
    },
  } as Plugin;
}

@pi0 what you think?

pi0 commented 1 year ago

Hi @jrson83 . Sorry for checking late on this. sourcemap support is now added for rollup. Plugin implementation seems nice. Do you still like to work on this feature?

jrson83 commented 1 year ago

Hi @jrson83 . Sorry for checking late on this. sourcemap support is now added for rollup. Plugin implementation seems nice. Do you still like to work on this feature?

Great, sure I will see what I can do and let you know 😀