Create a mix-manifest.json file with esbuild

• 2 min read

This website is very simple. It's just HTML and CSS. No JavaScript at all.
To generate the HTML, I (currently) use Jigsaw and Laravel Blade. For the CSS I use Tailwind CSS.

Up until a few weeks ago I've used Laravel Mix, and therefore Webpack, to compile the CSS file. I've been using Mix just for one feature: versioning.

By adding .version() to my configuration file, Laravel Mix would create a mix-manifest.json file when creating a new build. The contents of that file looks like this:

{
    "/css/main.css": "/css/main.css?id=9bdded1b8e8a06a52e67"
}

It contains a map of paths to CSS or JavaScript files where the path has been augmented with an id-parameter and a hashed value. The hashed value represents the checksum of the referenced file (css/main.css in this example) .

When creating a new build with Jigsaw, the generated HTML will load the CSS file with the hashed value. This makes cache busting super easy, as everytime the CSS file changes and new classes are added or removed, the hash changes. The browser will always load the up-to-date CSS file.

As mentioned, Laravel Mix is a wrapper around Webpack. Laravel Mix and Webpack are great. I'm thankful for their existence and all the work that has been put into in them in the last years. But Webpack is slow.

Building the CSS through Webpack takes between 1-2 seconds. Building the CSS with the Tailwind CLI directly drops this to 50ms. Switching to the CLI would be a big win on the performance side, but I would lose the cache busting feature.

On top of building the CSS file comes the time to create a new build with Jigsaw. A complete rebuild of this site takes between 3-5 seconds.

This long feedback loop got annoying when I tweaked the design. I switched from editor to browser too fast. I was still looking at the old design or content while the new version was building.

To fix this issue, I've switched from Laravel Mix to esbuild and wrote my own plugin to recreate the mix-manifest.json file.

I created a new build.js file that contains the following code:

#!/usr/bin/env node

const { build } = require('estrella');
const postcss = require('esbuild-postcss');
const mixManifestPlugin = require('@stefanzweifel/esbuild-mix-manifest-plugin');

build({
    entryPoints: {
        'css/main': 'resources/css/main.css',
    },
    outdir: 'source/assets/build',
    outbase: 'source/assets/build',
    metafile: true,
    minify: true,
    plugins: [postcss(), mixManifestPlugin()],
});

I'm using estrella to get the build method. I give the method my CSS file as an entrypoint, add the postcss and mixManifest plugins and that's it.

In my package.json file I've added 2 new script commands: One to create a new CSS-build. And a second command to watch the project for changes and compile the CSS and the site.

"scripts": {
    "prod": "node build.js",
    "watch": "nodemon --ignore source/assets/build/ --watch resources --watch source -e md,php,css  -x 'npm run prod && composer run build'"
},

Switching from Laravel Mix to esbuild cut the full build time of my site in half (from 4-5 seconds to 2 seconds).

If you work in a Laravel project that doesn't need a bundler like Webpack, Laravel Mix or Vite and you would like to have a simple way to cache bust your assets, give esbuild and my mix-manifest plugin a try.