designdebt.club
Menu
Design Debt Club

A Basic Custom esbuild Copy Plugin

Recently, in a project using esbuild, I found myself wanting to copy a single static file from my working folder into my dist folder with each build.

In searching online, I found a few plugins that supported copying files (namely esbuild-plugin-copy and esbuild-copy-static-files), but the problem I ran into was making them work with only one file in a directory of multiple files. Perhaps it was a lack of knowledge on my part, but I could only figure out how to tell it to copy all files in a folder, not just one single file.

So, I wrote a plugin myself. It’s super basic and specific to my needs, but it should be easily adaptable if you have a similar requirement.

The plugin code

Below is all of the plugin’s code, which is hardcoded to simply copy the file manifest.json from my projects root directory.

This was a requirement for me because the project is a plugin for Obsidian, and in that ecosystem, plugins require a manifest file in the final output. However, as part of the plugin submission process, the manifest file also needs to be in the root of your project directory, which means I couldn’t just put it into another folder and copy it from there.

import fs from 'fs';

const copyManifestPlugin = () => ({
    name: 'copy-manifest-plugin',
    setup(build) {
        build.onEnd(async () => {
            try {
                fs.cpSync('./manifest.json', './dist/manifest.json');
            } catch (e) {
                console.error('Failed to copy file:', e);
            }
        });
    },
});

Note: When I initially published this post I had used fs.copyFileSync above, but since then I’ve found that fs.copyFileSync doesn’t work in GitHub actions (Well, mine anyway), but fs.cpSync does. So use that.

Using the plugin

To use the above code, I wrote it directly in my esbuild.config.mjs, however, you could also put it in a separate file and import it in.

To call the plugin on build, it’s just like calling any other plugin – but since it’s functionality is hard coded, it has no settings.

esbuild.build({
	...
	plugins: [
		...
		copyManifestPlugin(),
	  ],
})

That’s it!

In many instances the existing copy plugins that are out there will do just fine, however, if you find yourself with a specific use case that doesn’t quite fit, hopefully this article showed you that esbuild plugins can be pretty easy.