I don't know why, it was working, I didn't change anything significant on my machine, it just stopped working. Weird part is that it still works on another machine!

So, something about how the watcher works (or doesn't in my case) - it's failing to detect filesystem events, apparently. Even more strangely, the watch-poll option doesn't work either.

When I run yarn watch or yarn watch-poll it'll compile once, immediately, and then sit there pretending to watch for changes, but never compile anything again.

SUPER annoying, and super frustrating. I lost a whole day chasing this, and ended up without any more insight than I started with.

What I did, eventually, end up with though - is a workaround of my own.

I have a .scripts directory in the root of my project for various dev scripts, namely a release script that updates my changelog file automatically from all the commits since the last release, thanks to Standard Version for making that painless (why oh why is it deprecated?!) - and also a hotfix script that will cherry pick specified commit(s) and push only those to the production branch.

My deployments are automatic thanks to github actions & laravel vapor, so everything really is quite a breeze working on this project... or was, until this issue turned up.

Anyway, the workaround. I'm sure I'm not the only person to have to fight with this, as I know there are issues with the way filesystem events on macOS work.. (or, again, don't).

In my .scripts directory I added a watch.js file:

const chokidar = require('chokidar');
const _debounce = require('lodash.debounce');
const path = require('node:path');
const { spawn } = require('child_process');

const mixPath = path.resolve(__dirname, '..', 'node_modules/.bin/mix');

const mix = function () {
  const output = spawn(mixPath, ['--no-progress']);
  output.stdout.pipe(process.stdout);
}


chokidar.watch([
   path.resolve(__dirname, '..', 'resources/views/**/*.blade.php'),
   path.resolve(__dirname, '..', 'storage/framework/views/**/*.php'),
   path.resolve(__dirname, '..', 'vendor/**/*.blade.php'),
]).on('all', _debounce(mix, 150));

The relevant dependencies should already be installed with laravel mix & webpack, but just to be sure you might want to:
npm install -D chokidar lodash.debounce

I then added a new item to the scripts in the package.json:
"watch-chokidar": "node ./.scripts/watch.js"

Running that command, watching works as expected once again, and properly re-triggers the mix command any time a file is added, removed, changed, etc.

If you find that it triggers more than once, especially on first run, try increasing the debounce time from 150 to something higher.

Enjoy, hope it helps someone else, and if not it felt good to vent a little bit. 😎

EDIT: Just a week or two later, vite has replaced mix as the preferred/default bundler, and this is no longer a concern - also WOW it's way faster!