oven-sh / bun

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

Plugin api enhancement: PluginBuilder.onEnd / PluginBuilder.onFinalize #15068

Open AFE-GmdG opened 2 weeks ago

AFE-GmdG commented 2 weeks ago

What is the problem this feature would solve?

I want to write a plugin to find unused files in the source directory.

I could enumerate all files below any entrypoint I could cross out all used files I couldn't store the result to for example a json string array file. There is no builder.onEnd or ofFinalize method to hook to which is called after the compiling process. This could also be interesting for example to sign code or create a setup etc.

import Bun, { BunPlugin, PluginBuilder, Glob } from "bun";

import path from "node:path";

type InterceptPluginOptions = {
  ignorePattern?: string[];
};

const findUnusedFilePlugin = ({
  ignorePattern = [],
}: InterceptPluginOptions): BunPlugin => {
  const allSourceFileNames = new Set<string>();

  return {
    name: "intercept-plugin",
    async setup (builder: PluginBuilder) {
      if (builder.config.outdir == null || builder.config.entrypoints.length === 0) {
        return;
      }

      const allSourcePaths = new Set<string>();
      builder.config.entrypoints.forEach((entryPoint) => {
        allSourcePaths.add(path.dirname(entryPoint));
      });

      const allGlob = new Glob("**");
      for (const directory of allSourcePaths) {
        for await (const file of allGlob.scan({
          cwd: directory,
          dot: true,
          absolute: true,
          followSymlinks: true,
        })) {
          // Test the file against the ignore patterns
          for (const pattern of ignorePattern) {
            const patternGlob = new Glob(
              pattern.startsWith("**/")
                ? pattern
                : `**/${pattern}`
              );
            if (patternGlob.match(file)) {
              // console.log(`Ignoring file: ${file}`);
              continue;
            }
          }

          // console.log(`Intercepting file: ${file}`);
          allSourceFileNames.add(file);
        }
      };

      builder.onLoad({ filter: /.*/, namespace: "file" }, async ({ path, loader }) => {
        // Remove from the set
        allSourceFileNames.delete(path);

        const file = Bun.file(path);
        const contents = await file.text();
        // console.log({ content: contents });
        return {
          contents,
          loader,
        };
      });
    },
  };
};

export default findUnusedFilePlugin;

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

Add a method onEnd / onFinalize to the PluginBuilder interface, that is called after the compiling process

What alternatives have you considered?

No response

BjornTheProgrammer commented 1 week ago

This is potentially a duplicate of #2771.