Run package.json scripts upon any file changes in a folder

By

Learn how to re-run a package.json script automatically whenever a file in a folder changes, using nodemon to rebuild on every save.

~~~

To run a package.json script whenever a file changes in a folder, we can use nodemon. Most people know it as the tool that restarts a Node.js server, but it can run any command when files change.

Older versions of this post used the watch npm package, which hasn’t had a release since 2017.

My practical problem: I want to automatically regenerate the CSS, going through a PostCSS pipeline, upon file changes.

The approach I describe works for any kind of automatic file and folder watching, not just for this specific case.

I have this script in action, which I run using yarn build:css:

"scripts": {
  "build:css": "postcss src/tailwind.css -o static/dist/tailwind.css"
}

and I want to re-run it whenever something changes in the layouts folder, which contains all the HTML files that build up my site.

Every time I change something in there, I want to regenerate the CSS and trigger the purge and minification I set up in PostCSS.

How to do this?

Install nodemon as a dev dependency:

npm install --save-dev nodemon

and add a watch script to your package.json file. You already had build:css from before. We just add a script that watches the layouts folder and runs build:css upon every change:

"scripts": {
  "build:css": "postcss src/tailwind.css -o static/dist/tailwind.css",
  "watch": "nodemon --watch ./layouts --ext html,js,css --exec \"npm run build:css\""
}

Now run npm run watch or yarn watch. Save any file inside layouts, and the CSS is rebuilt.

How the command works

Here’s what each flag does:

nodemon also runs the command once when it starts, so you get a fresh build right away. Then it prints clean exit - waiting for changes before restart and waits. If you don’t want that first run, add --on-change-only.

You can watch more than one folder by repeating --watch:

"watch": "nodemon --watch ./layouts --watch ./content --ext html,js,css --exec \"npm run build:css\""

Watch out for the quotes

Wrap the --exec command in quotes, so it reaches nodemon as a single argument.

Without quotes, nodemon glues the leftover words back together, so --exec npm run build:css happens to work. The trouble starts when the command has flags of its own, like the Tailwind CLI:

nodemon --watch ./layouts --exec tailwindcss -i src/input.css -o dist/output.css

-i is also nodemon’s shortcut for --ignore. nodemon takes -i src/input.css for itself and runs tailwindcss -o dist/output.css, with no input file and no error to warn you.

The script already lives inside a JSON string delimited by double quotes, so we escape the inner quotes as \". Single quotes would look cleaner, but they don’t group arguments in the Windows command prompt.

Too many rebuilds?

Some editors save a file multiple times in a row, and each save triggers the command. If your build is slow, that piles up.

The --delay flag helps. nodemon waits for that many seconds after the last change before it runs the command:

"watch": "nodemon --watch ./layouts --ext html,js,css --delay 2 --exec \"npm run build:css\""

With this, a burst of saves results in one rebuild instead of five.

What about node --watch?

If what you want to restart is a Node.js script, like a server, you don’t need any package. Node.js has a built-in watch mode:

node --watch server.js

It restarts the process when server.js or any file it imports changes. It only runs Node.js files, though, so for a command like npm run build:css we still need nodemon.

Tagged: Node.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about node: