oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
74.12k stars 2.76k forks source link

Build plugin feat: add `entrypoint` field to args in `onLoad` callback #15003

Open aralroca opened 2 days ago

aralroca commented 2 days ago

What is the problem this feature would solve?

Currently, in Bun build plugins, tracking which files belong to which entry points during the build process is difficult. This limitation complicates dependency analysis, as there is no straightforward way to determine the original entrypoint context for each file within the onLoad callback.

What is the feature you are proposing to solve the problem?

I propose adding an entrypoint field to the args object within the onLoad callback. This field would contain the path of the original entrypoint associated with each file being processed. By having this reference available in onLoad, plugin developers can easily associate files with their respective entrypoints, making it simpler to build accurate dependency trees and perform entrypoint-specific analyses.

build.onLoad({ filter: /.*/ }, async ({ path, loader, entrypoint, namespace }) => {
   console.log({ entrypoint })
   // ...
});

What alternatives have you considered?

One alternative is to manually track entrypoints in onResolve and then try to match each loaded file back to an entrypoint based on its path. However, this approach becomes complicated and error-prone as the complexity of the project increases, especially with dynamic imports or multiple layers of dependencies. Another option would be to use custom properties or metadata on each file, but this requires additional bookkeeping and does not integrate cleanly with the build process. Adding the entrypoint field directly in onLoadwould provide a more robust and native solution within esbuild’s existing framework.

aralroca commented 2 days ago

I wonder if currently, file execution is deterministic. If it could be solved by detecting the entrypoint in the onResolve and using it in the onLoad... Can we trust this? 🤔

function loadPlugin() {
  const analysisPerEntrypoint: Record<string, string[]> = {};

  const plugin: BunPlugin = {
      name: 'some-plugin',
      setup(build) {
        const entrypointsSet = new Set(build.config.entrypoints);
        let entrypoint = build.config.entrypoints[0];

        build.onResolve({ filter: /.*/ }, async ({ path, namespace, importer }) => {
          // Detect entrypoint here...
          if(entrypointsSet.has(importer) && entrypoint !== importer) {
            entrypoint = importer;
          }

          return { path, namespace }
        });
        build.onLoad({ filter: /.*/  },
          async ({ path, loader, namespace }) => {
            let code = await Bun.file(path).text();

            // ... some stuff

           // Consume entrypoint here
            if(!analysisPerEntrypoint[entrypoint]) analysisPerEntrypoint[entrypoint] = []
            analysisPerEntrypoint[entrypoint].push(/* something */)

            return {
              contents: code,
              loader,
            };
          },
        );
    }
  }

  return { plugin, getAnalysisPerEntrypoint: () => analysisPerEntrypoint }
}