Saturday, October 12, 2024
HomeRuby On RailsTune up your esbuild config with plugins and cleanup your property listing

Tune up your esbuild config with plugins and cleanup your property listing


In my earlier publish, I wrote about migrating a big JS mission from Webpack to esbuild. I discussed that the method shouldn’t be absolutely optimum as previous information from earlier builds are stored within the app/property/builds listing. This isn’t an issue while you’re not utilizing the splitting choice with hashed chunk names. However if you wish to cut up your bundle into extra smaller chunks and lazy-load them, you’ll find yourself with a number of small JS information with digested names that will change typically in your app/property/builds listing. Which may lead to sluggish preliminary requests after beginning the Rails server. I spent a while studying about esbuild plugins and it turned out {that a} answer for this problem may be very easy.

Esbuild plugin system permits working on-end callbacks when a construct ends. It is a excellent place to hook our cleanup activity that can take away all information from earlier builds which can be not wanted. The preliminary model of the plugin could appear to be this:

const fs = require("fs");
const path = require("path");
const glob = require("glob");
const esbuild = require("esbuild");

operate cleanup({sample = "*"}) {
  return {
    identify: "esbuild:cleanup",
    setup(construct) {
      const choices = construct.initialOptions;
      construct.onEnd(async (outcome) => {
        const safelist = new Set(Object.keys(outcome.metafile.outputs));
        await glob(path.be a part of(choices.outdir, sample), (err, information) => {
          information.forEach((path) => {
            if (!safelist.has(path))
              fs.unlink(path);
          });
        });
      });
    },
  };
}

esbuild.construct({
  // some config,
  metafile: true,
  plugins: [cleanup()]
})

Within the onEnd callback, we’re utilizing the metafile property to get the record of all output entries created in the course of the construct and add it to the safelist set. Then we’re utilizing the glob bundle to record all information in our output listing. I’m studying the outdir setting from the preliminary construct configuration and appending the * sample as I don’t have any subdirectories within the output listing. When you’ve got one other setup, you may use a unique sample (or simply present the sample by yourself, with out utilizing the construct choices). When the record is prepared, we are able to iterate over it and take away all information that aren’t current in our safelist set.

What about preserving some information?

The proposed plugin may be sufficient for easy use instances the place you simply use a single esbuild construct and don’t put some other information within the app/property/builds listing. However let’s assume you’re utilizing Tailwind CSS to arrange your stylesheets and also you’re additionally placing them in your app/property/builds listing. Or you may have a number of esbuild builds since you put together property in several codecs (e.g. ESM and IIFE for older browsers). In such state of affairs, you’ll want some safelist to make sure you gained’t take away information generated by different instruments or in different builds.

Extending the preliminary plugin can also be simple – we simply must cross a safelist when developing the plugin:

operate cleanup({sample = "*", safelist = []}) {
  return {
    identify: "esbuild:cleanup",
    setup(construct) {
      const choices = construct.initialOptions;
      const safelistSet = new Set(safelist);
      construct.onEnd(async (outcome) => {
        Object.keys(outcome.metafile.outputs).forEach((path) =>
          safelistSet.add(path)
        );
        await glob(path.be a part of(choices.outdir, sample), (err, information) => {
          information.forEach((path) => {
            if (!safelistSet.has(path))
              fs.unlink(path);
          });
        });
      });
    },
  };
}

esbuild.construct({
  // some config,
  metafile: true,
  plugins: [cleanup(["app/assets/builds/style.css"])]
})

Now we’ll maintain all information from the construct and likewise protect information from the safelist. For those who’re utilizing multiple esbuild construct in your setup, you possibly can add the cleanup plugin to the final construct and retrieve all information generated in earlier builds from the metafile:

const iifeBuild = await esbuild.construct(iifeConfig); // keep in mind about setting metafile to true
const esmBuild = await esbuild.construct({
  // ...
  metafile: true,
  plugins: [cleanup(Object.keys(iifeBuild.metafile.outputs))]
})

Including some logging

The plugin is now prepared however because it’s most helpful in growth when incremental builds are enabled, we are able to add some logging to see what’s occurring in the course of the cleanup and likewise stop from working the plugin in environments that aren’t accurately configured (e.g. when metafile shouldn’t be out there). You too can use make use of console.time methodology to trace how lengthy the cleanup takes. Right here’s my proposal for the plugin with the safelist and logging:

operate cleanup({sample = "*", safelist = [], debug = false}) {
  return {
    identify: "esbuild:cleanup",
    setup(construct) {
      const choices = construct.initialOptions;
      if (!choices.outdir) {
        console.log("[esbuild cleanup] Not outdir configured - skipping the cleanup");
        return;
      }
      if (!choices.metafile) {
        console.log("[esbuild cleanup] Metafile shouldn't be enabled - skipping the cleanup");
        return;
      }
      const safelistSet = new Set(safelist);
      construct.onEnd(async (outcome) => {
        attempt {
          console.time("[esbuild cleanup] Cleansing up previous property");
          Object.keys(outcome.metafile.outputs).forEach((path) =>
            safelistSet.add(path)
          );
          await glob(path.be a part of(choices.outdir, sample), (err, information) => {
            information.forEach((path) => {
              if (!safelistSet.has(path))
                fs.unlink(
                  path,
                  (err) =>
                    debug &&
                    console.log(
                      err
                        ? "[esbuild cleanup] " + err
                        : "[esbuild cleanup] Eliminated previous file: " + path
                    )
                );
            });
          });
        } lastly {
          console.timeEnd("[esbuild cleanup] Cleansing up previous property");
        }
      });
    },
  };
}

The esbuild plugin system is model new and remains to be actively developed. This implies the API may be modified sooner or later so earlier than you employ this code in your mission, ensure it’s nonetheless appropriate together with your esbuild model – I used to be utilizing esbuild 0.14.54.



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments