bridgetownrb / tailwindcss-automation

Automation for installing TailwindCSS in Bridgetown sites
MIT License
11 stars 2 forks source link

Requiring jit-refresh.css breaks some automated builds #3

Open christopher-b opened 1 month ago

christopher-b commented 1 month ago

The installation process for this automation creates the file frontend/styles/jit-refresh.css, and adds that file to .gitignore. If a build process is being run in a CI environment, or using something like Kamal which checks out a fresh copy of the repo, the jit-refresh.css file will not be present in that build environment, and the @import statement referencing this file will fail. A frontend build process will complete successfully (the exit code will be zero), but an error will be emitted:

> node esbuild.config.js --minify

esbuild: frontend bundling started...

error: "undefined" while processing CSS file:
undefined:undefined:undefined

esbuild: frontend bundling complete!
esbuild: entrypoints processed:
         - index.P6KJZRZR.js: 113B
         - index.DYSKJ26J.css: 3.9KB

Any CSS below the line @import "jit-refresh.css"; will not be included in the build artifacts, which in this case is... all of Tailwind 😁

One workaround in to include touch frontend/styles/jit-refresh.css in the build process, before building the frontend.

I wonder if there is another way to trigger Tailwind's JIT in development that doesn't break a build in a fresh checkout of the repo.

christopher-b commented 1 month ago

I do see that touch frontend/styles/jit-refresh.css gets added to the frontend:build rake task, so the file exists in that context.

I'm calling npm run esbuild directly in my Dockerfile directly, as per these instructions. Ruby is not available in that environment, so we can't call the rake task there.

One solution is to just touch the file in the Dockerfile:

...
RUN touch frontend/styles/jit-refresh.css
RUN npm run esbuild
...

Antoher solution is to ensure the file is touch'ed in the esbuild process. This is a bit more complicated, but ensures the file exists regardless of how esbuild is triggered.

We can create an esbuild plugin to touch a file:

// plugins/touch_file.js
const fs = require("fs");

const createTouchFilePlugin = (filePaths) => ({
  name: "touch-file",
  setup(build) {
    build.onStart(() => {
      if (!Array.isArray(filePaths) || filePaths.length === 0) {
        console.warn("No file paths provided to touch.");
        return;
      }

      const time = new Date();

      filePaths.forEach((filePath) => {
        try {
          fs.utimesSync(filePath, time, time);
        } catch (err) {
          fs.closeSync(fs.openSync(filePath, "w"));
        }
        console.log(`Touched file at ${filePath}`);
      });
    });
  }
});

module.exports = createTouchFilePlugin;

And add this to the esbuild config:

// esbuild.config.js
const esbuildOptions = {
  plugins: [
    touchFilePlugin(["frontend/styles/jit-refresh.css"])
  ],
  ...
}

If this makes sense, I can submit a PR to add this the automation. Or perhaps it could just live in the documentation for others who hit this problem.