CLI tool that automatically runs npm scripts when files change using configurable file watching patterns
npx @tessl/cli install tessl/npm-npm-watch@0.13.0npm-watch is a CLI tool and Node.js module that automatically runs npm scripts when files in your project change. It wraps nodemon to provide configuration-driven file watching, enabling development workflows with automatic test execution, live reloading, and build processes.
npm install npm-watchFor programmatic usage:
// Direct import of the main module (recommended)
const watchPackage = require("npm-watch/watch-package");Important: The package.json specifies "main": "index.js" but this file doesn't exist in the package. The actual entry point is watch-package.js, so you must specify the full path when requiring the module programmatically.
Add a "watch" configuration to your package.json and a watch script:
{
"watch": {
"test": "{src,test}/*.js",
"build": "src/**/*.js"
},
"scripts": {
"test": "mocha test/*.js",
"build": "webpack --mode=development",
"watch": "npm-watch"
}
}Run the watcher:
npm run watchconst watchPackage = require("npm-watch/watch-package");
// Watch all configured tasks
const watcher = watchPackage(
process.cwd(), // directory containing package.json
process.exit, // exit callback
"" // empty string watches all tasks
);
// Handle input/output streams
process.stdin.pipe(watcher);
watcher.stdout.pipe(process.stdout);
watcher.stderr.pipe(process.stderr);
// Watch a specific task only
const specificWatcher = watchPackage(
"/path/to/project", // project directory
(code) => { // custom exit handler
console.log(`Watcher exited with code: ${code}`);
process.exit(code);
},
"test" // watch only the "test" script
);Advanced Example with Custom Stream Handling:
const watchPackage = require("npm-watch/watch-package");
const fs = require("fs");
const watcher = watchPackage(process.cwd(), process.exit, "");
// Log all output to a file
const logStream = fs.createWriteStream("watch.log", { flags: "a" });
watcher.stdout.pipe(logStream);
watcher.stderr.pipe(logStream);
// Also display to console
watcher.stdout.pipe(process.stdout);
watcher.stderr.pipe(process.stderr);
// Handle stdin for interactive restart commands
process.stdin.pipe(watcher);npm-watch consists of several key components:
Command-line interface for running npm-watch. The CLI automatically adds node_modules/.bin to the PATH before executing.
npm-watch [taskName] [directory]Parameters:
taskName (optional): Specific script name from package.json to watch (process.argv[2])directory (optional): Directory to watch (process.argv[3], defaults to current working directory)Interactive Commands:
rs: Restart all watching processesrs <taskName>: Restart specific watching processEnvironment Setup: The CLI automatically handles cross-platform PATH setup:
<packageDir>/node_modules/.bin to PATH/Path environment variableCore programmatic interface for creating file watchers.
/**
* Creates a watcher for npm scripts based on package.json configuration
* @param {string} pkgDir - Directory containing package.json file
* @param {function} exit - Exit callback function, called with error code on failure
* @param {string} taskName - Specific task to watch; empty string or undefined watches all configured tasks
* @returns {Stream} Transform stream (through2) for stdin handling with additional stdout/stderr properties
*/
function watchPackage(pkgDir, exit, taskName)Return Value Properties:
.stdout: Transform stream for script output (through2 stream).stderr: Transform stream for script errors (through2 stream)Input Commands:
rs\n: Restart all watching processesrs <taskName>\n: Restart specific watching process by nameError Handling: The function validates configuration and calls the exit callback with error codes:
Process Management: Creates nodemon child processes for each configured watch task, manages their stdio streams, and handles process cleanup on termination.
These functions are used internally but may be useful for understanding the implementation:
/**
* Creates a transform stream that prefixes output lines with a given prefix
* Used internally to distinguish output from different watch tasks
* @param {string} prefix - The prefix to add to each line (e.g., "[test]")
* @returns {Stream} Transform stream that prefixes each line
*/
function prefixer(prefix)
/**
* Starts a nodemon process for a specific script with watch configuration
* Used internally by watchPackage to create individual watchers
* @param {string} script - Script name from package.json
* @param {object} pkg - Parsed package.json content
* @param {object} processes - Process registry for tracking spawned processes
* @param {boolean} setMaxListeners - Whether to set max listeners for event emitters
* @param {number} scriptsCount - Total number of scripts for listener calculation
*/
function startScript(script, pkg, processes, setMaxListeners, scriptsCount)npm-watch reads configuration from the "watch" object in package.json.
interface SimpleWatchConfig {
[scriptName: string]: string | string[];
}Example:
{
"watch": {
"test": "src/**/*.js",
"build": ["src", "config"]
}
}interface AdvancedWatchConfig {
[scriptName: string]: {
patterns: string | string[]; // Glob patterns to watch
extensions?: string; // Comma-separated file extensions
ignore?: string | string[]; // Patterns to ignore
quiet?: boolean; // Hide script name in output
inherit?: boolean; // Inherit parent process stdio
legacyWatch?: boolean; // Enable nodemon legacy watch mode
delay?: number; // Restart delay in milliseconds
clearBuffer?: boolean; // Clear output buffer on restart
verbose?: boolean; // Enable nodemon verbose mode
runOnChangeOnly?: boolean; // Run only on file changes, not startup
silent?: boolean; // Enable nodemon silent mode
};
}Example:
{
"watch": {
"test": {
"patterns": ["src", "test"],
"extensions": "js,jsx,ts",
"ignore": ["build", "node_modules"],
"quiet": false,
"legacyWatch": false,
"delay": 1000,
"runOnChangeOnly": true
},
"build": {
"patterns": ["src/**/*.js"],
"extensions": "js,ts",
"clearBuffer": true,
"verbose": false,
"silent": false
},
"lint": {
"patterns": "src",
"extensions": "js,jsx,ts,tsx",
"inherit": true,
"ignore": "src/vendor"
}
}
}Real-world Configuration Examples:
{
"watch": {
"test:unit": {
"patterns": ["src", "test/unit"],
"extensions": "js,ts",
"runOnChangeOnly": true,
"quiet": true
},
"test:integration": {
"patterns": ["src", "test/integration"],
"extensions": "js,ts",
"delay": 2000
},
"build:dev": {
"patterns": "src",
"extensions": "js,jsx,ts,tsx",
"clearBuffer": true,
"ignore": ["src/**/*.test.js", "src/**/*.spec.js"]
}
},
"scripts": {
"test:unit": "jest --testPathPattern=unit",
"test:integration": "jest --testPathPattern=integration",
"build:dev": "webpack --mode=development"
}
}interface WatchGlobalConfig {
setMaxListeners?: boolean; // Set max event listeners to avoid warnings
}Applied to package.json as:
{
"watchGlobalConfig": {
"setMaxListeners": true
}
}npm-watch provides several types of error handling:
"watch" configuration exists in package.json"scripts" sectionCommon Errors:
No "watch" config in package.json: Missing watch configurationNo such script "scriptName": Referenced script doesn't existSilent and Verbose can not both be on: Conflicting configuration optionsnpm-watch provides cross-platform compatibility:
.cmd variants of executables (npm.cmd, nodemon.cmd) and handles PATH differencesnpm-watch relies on these key dependencies:
Monorepo Setups: In monorepo environments, npm-watch may fail with ENOENT errors. This typically occurs when nodemon is not available in the expected location. The solution is to install nodemon globally:
npm install -g nodemonPATH Issues: The CLI automatically adds node_modules/.bin to PATH, but in some environments this may not work as expected. Ensure nodemon is installed as a dependency or globally available.
Memory Warnings: If you see "MaxListenersExceededWarning" messages, add the global configuration:
{
"watchGlobalConfig": {
"setMaxListeners": true
}
}