Library / SDK
Qard/onchange avatar
Qard/onchange

onchange: run a command when files matching a glob change

Use glob patterns to watch file sets and run a command when anything is added, changed or deleted.

826 stars48 forksTypeScriptMIT

At a glance

What is it?
A small TypeScript watcher that takes glob patterns and a command, then runs that command when anything is added, changed or deleted. It handles the fiddly parts of file watching, such as killing the previous process and waiting for writes to settle.
Who is it for?
onchange suits a project that wants a single command rerun on a change, without writing a watcher script and without the ceremony of a bundler plugin, and its template variables make it easy to pass the changed path to the command. It does not suit a team that wants file watching with per-file type handling, since every event runs the same command.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 129 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Globs in, one command out

onchange watches file sets described by glob patterns and runs a command when anything in them is added, changed or deleted. The shape of it is a pattern list, then a double dash, then the command:

sh
npm install onchange

A typical invocation watches two source trees and reruns the test suite on any change:

sh
onchange 'app/**/*.js' 'test/**/*.js' -- npm test

You can match as many patterns as you like, and the command runs whenever a file matching any of them changes. The implementation sits on chokidar for the watching, with cross-spawn for running the child process, tree-kill for terminating process trees, the ignore package for path filtering, and a small queue built on @blakeembrey/deque. The command is passed through a template engine, @blakeembrey/template, which is what makes the path substitutions work.

One practical note sits in the README: Windows users may need double quotes instead of single quotes, and if the command is used in an npm script the double quotes have to be escaped, which is the kind of detail that costs an afternoon once.

Template variables carry the changed file into the command

The command is a template rather than a literal string, and the variables available are listed in the README. The two you will use most are event and file, so the simplest possible example is:

sh
onchange '**/*.js' -- echo '{{event}} to {{file}}'

Beyond those there are four derived from the path. fileExt is the extension as returned by path.extname, fileBase is the basename, fileBaseNoExt is the basename without the extension, and fileDir is the directory name from path.dirname. That set is enough to pass a filename to a tool that expects a path, or to construct a target filename in another directory, which is what the jobs example does by copying the changed file into a test tree. The variables are substituted per event, so a burst of changes produces a run per change unless you constrain the concurrency.

Three ways to keep files out of the way

Exclusion is handled at three levels. The most obvious is the exclude flag, which takes a glob pattern and drops matching files, and the README's example watches TypeScript sources while ignoring compiled output in dist. The second is the inverse: node_modules and .git are excluded by default, and --no-exclude turns that default off, which is what you need if you genuinely want to watch inside node_modules. The third is exclude-path, which points the tool at a file that follows the .gitignore specification, so your repository's existing ignore rules become the watch rules:

sh
onchange '**' --exclude-path .gitignore -- prettier

The ignore package does the work here, which is the same library gitignore-style matching tools use, so the semantics match what a developer expects from the file they already maintain.

Process control is where this differs from a naive watcher

The options that separate onchange from a two-line script are about what happens to processes and to events. The kill flag, -k, kills the current and any pending processes between changes, which is the difference between restarting a server and stacking servers on each other. The jobs option, -j with a number, sets the maximum number of concurrent processes, defaulting to one, so a burst of ten changes does not launch ten test runs at once. The delay option, -d in milliseconds, spaces out the process changes. The await-write-finish option holds events until the file size stops changing for a configurable period, defaulting to 2000 milliseconds, which is the fix for the editor that writes a file in several chunks and triggers your build on the first one. The poll option switches from native file system events to polling, which the README says is useful when watching an NFS volume.

Filtering events and piping output elsewhere

By default the tool reacts to all event types chokidar reports. The filter flag, -f, restricts that to the events you care about, and the README shows watching only additions and changes. The outpipe option, -o, takes a shell command that runs on every change, which turns the tool into a general event notifier as well as a command runner, and the example redirects the output of the watched command into a changelog file with the shell redirection inside the argument:

sh
onchange -o '> .changelog' 'src/**/*.js' -- echo '{{event}} to {{file}}'

The README adds a note that when a command is used with outpipe, the stdout of the command is piped into the outpipe command, so the two outputs are chained rather than merely sequential. The initial flag, -i, runs the command once on load without any event, which is how a long running server starts before the first change arrives, and the add flag, -a, runs the command for all paths that were initially present.

Verbose logging is available with -v for debugging the watcher itself, and the package ships TypeScript types, so a project on TypeScript does not need to declare its own module shape.

A 2020 release line with a 2026 push

The maintenance picture needs two numbers read together. The repository is not archived and the last push was on 2026-06-18, so somebody is still touching the code. The tagged releases, though, are v7.1.0 from 2020-10-22, which added file replacements, v7.0.2 from 2020-04-30, which fixed the exclude path handling, and v7.0.1 from 2020-04-28, which fixed the command line exclude. The package manifest still says version 7.1.0, and it depends on chokidar 3.6.0, which is the older major line of the watcher. The licence is MIT with the licence file in the repository root, and the author is Stephen Belanger. The build is handled by ts-scripts from @borderless/ts-scripts, the local test suite runs two node scripts called queue.js and concurrent.js, and there is a SECURITY.md in the repository. For anyone pinning a dependency, the practical takeaway is to treat 7.1.0 as the current published version and read its changelog rather than assuming recent commits mean a recent release.

Editorial conclusion

onchange suits a project that wants a single command rerun on a change, without writing a watcher script and without the ceremony of a bundler plugin, and its template variables make it easy to pass the changed path to the command. It does not suit a team that wants file watching with per-file type handling, since every event runs the same command. Before adopting it, remember that the tagged releases stop at 7.1.0 in 2020 even though the repository was pushed to in June 2026, so pin the version and check the chokidar behaviour you depend on.

Frequently asked questions

How do I install onchange?

Run npm install onchange. It ships as a command line tool with the bin entry onchange pointing at dist/bin.js.

How do I run a command whenever a matching file changes?

Put the glob patterns before a double dash and the command after it, for example onchange 'app/**/*.js' 'test/**/*.js' -- npm test. The command runs whenever a file matching any of the patterns is added, changed or deleted.

Which template variables can I use in the onchange command?

The README lists event, file, fileExt from path.extname, fileBase, fileBaseNoExt and fileDir from path.dirname.

How do I exclude node_modules and .git from onchange watching?

Both are excluded by default, and the --no-exclude flag disables that behaviour. The --exclude-path option additionally takes a file that follows the .gitignore specification, such as onchange '**' --exclude-path .gitignore -- prettier.

How do I limit how many commands onchange runs at once?

Use the jobs option, -j or --jobs with a number, which sets the maximum concurrent processes. The default is 1, and the -k flag kills current and pending processes between changes.

Official sources

  1. Issues
  2. License: MIT
  3. Qard/onchange on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/qard-onchange.svg)](https://hysenlabs.com/projects/qard-onchange)

Was this useful?