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.00
# npm-watch
1
2
npm-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.
3
4
## Package Information
5
6
- **Package Name**: npm-watch
7
- **Package Type**: npm
8
- **Language**: JavaScript
9
- **Installation**: `npm install npm-watch`
10
11
## Core Imports
12
13
For programmatic usage:
14
15
```javascript
16
// Direct import of the main module (recommended)
17
const watchPackage = require("npm-watch/watch-package");
18
```
19
20
**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.
21
22
## Basic Usage
23
24
### CLI Usage
25
26
Add a `"watch"` configuration to your `package.json` and a watch script:
27
28
```json
29
{
30
"watch": {
31
"test": "{src,test}/*.js",
32
"build": "src/**/*.js"
33
},
34
"scripts": {
35
"test": "mocha test/*.js",
36
"build": "webpack --mode=development",
37
"watch": "npm-watch"
38
}
39
}
40
```
41
42
Run the watcher:
43
44
```bash
45
npm run watch
46
```
47
48
### Programmatic Usage
49
50
```javascript
51
const watchPackage = require("npm-watch/watch-package");
52
53
// Watch all configured tasks
54
const watcher = watchPackage(
55
process.cwd(), // directory containing package.json
56
process.exit, // exit callback
57
"" // empty string watches all tasks
58
);
59
60
// Handle input/output streams
61
process.stdin.pipe(watcher);
62
watcher.stdout.pipe(process.stdout);
63
watcher.stderr.pipe(process.stderr);
64
65
// Watch a specific task only
66
const specificWatcher = watchPackage(
67
"/path/to/project", // project directory
68
(code) => { // custom exit handler
69
console.log(`Watcher exited with code: ${code}`);
70
process.exit(code);
71
},
72
"test" // watch only the "test" script
73
);
74
```
75
76
**Advanced Example with Custom Stream Handling:**
77
78
```javascript
79
const watchPackage = require("npm-watch/watch-package");
80
const fs = require("fs");
81
82
const watcher = watchPackage(process.cwd(), process.exit, "");
83
84
// Log all output to a file
85
const logStream = fs.createWriteStream("watch.log", { flags: "a" });
86
watcher.stdout.pipe(logStream);
87
watcher.stderr.pipe(logStream);
88
89
// Also display to console
90
watcher.stdout.pipe(process.stdout);
91
watcher.stderr.pipe(process.stderr);
92
93
// Handle stdin for interactive restart commands
94
process.stdin.pipe(watcher);
95
```
96
97
## Architecture
98
99
npm-watch consists of several key components:
100
101
- **CLI Interface**: Command-line wrapper that sets up PATH and invokes the main module
102
- **Watch Engine**: Main module that reads package.json configuration and manages file watchers
103
- **Process Management**: Spawns and manages nodemon processes for each watch task
104
- **Stream Handling**: Manages stdin/stdout/stderr pipes and input processing
105
- **Cross-Platform Support**: Handles platform differences for Windows and Unix-like systems
106
107
## Capabilities
108
109
### CLI Binary
110
111
Command-line interface for running npm-watch. The CLI automatically adds `node_modules/.bin` to the PATH before executing.
112
113
```bash { .api }
114
npm-watch [taskName] [directory]
115
```
116
117
**Parameters:**
118
- `taskName` (optional): Specific script name from package.json to watch (process.argv[2])
119
- `directory` (optional): Directory to watch (process.argv[3], defaults to current working directory)
120
121
**Interactive Commands:**
122
- `rs`: Restart all watching processes
123
- `rs <taskName>`: Restart specific watching process
124
125
**Environment Setup:**
126
The CLI automatically handles cross-platform PATH setup:
127
- Adds `<packageDir>/node_modules/.bin` to PATH/Path environment variable
128
- Uses correct executable names for Windows (.cmd) vs Unix systems
129
- Pipes stdin/stdout/stderr between the main process and watch-package module
130
131
### Main Module Export
132
133
Core programmatic interface for creating file watchers.
134
135
```javascript { .api }
136
/**
137
* Creates a watcher for npm scripts based on package.json configuration
138
* @param {string} pkgDir - Directory containing package.json file
139
* @param {function} exit - Exit callback function, called with error code on failure
140
* @param {string} taskName - Specific task to watch; empty string or undefined watches all configured tasks
141
* @returns {Stream} Transform stream (through2) for stdin handling with additional stdout/stderr properties
142
*/
143
function watchPackage(pkgDir, exit, taskName)
144
```
145
146
**Return Value Properties:**
147
- `.stdout`: Transform stream for script output (through2 stream)
148
- `.stderr`: Transform stream for script errors (through2 stream)
149
- Stream itself: Handles stdin input for interactive commands like "rs"
150
151
**Input Commands:**
152
- `rs\n`: Restart all watching processes
153
- `rs <taskName>\n`: Restart specific watching process by name
154
155
**Error Handling:**
156
The function validates configuration and calls the exit callback with error codes:
157
- Code 1: Missing "watch" configuration in package.json
158
- Code 2: Specified script doesn't exist in package.json scripts section
159
160
**Process Management:**
161
Creates nodemon child processes for each configured watch task, manages their stdio streams, and handles process cleanup on termination.
162
163
### Internal Utilities
164
165
These functions are used internally but may be useful for understanding the implementation:
166
167
```javascript { .api }
168
/**
169
* Creates a transform stream that prefixes output lines with a given prefix
170
* Used internally to distinguish output from different watch tasks
171
* @param {string} prefix - The prefix to add to each line (e.g., "[test]")
172
* @returns {Stream} Transform stream that prefixes each line
173
*/
174
function prefixer(prefix)
175
176
/**
177
* Starts a nodemon process for a specific script with watch configuration
178
* Used internally by watchPackage to create individual watchers
179
* @param {string} script - Script name from package.json
180
* @param {object} pkg - Parsed package.json content
181
* @param {object} processes - Process registry for tracking spawned processes
182
* @param {boolean} setMaxListeners - Whether to set max listeners for event emitters
183
* @param {number} scriptsCount - Total number of scripts for listener calculation
184
*/
185
function startScript(script, pkg, processes, setMaxListeners, scriptsCount)
186
```
187
188
### Configuration Schema
189
190
npm-watch reads configuration from the `"watch"` object in package.json.
191
192
#### Simple Pattern Format
193
194
```javascript { .api }
195
interface SimpleWatchConfig {
196
[scriptName: string]: string | string[];
197
}
198
```
199
200
**Example:**
201
```json
202
{
203
"watch": {
204
"test": "src/**/*.js",
205
"build": ["src", "config"]
206
}
207
}
208
```
209
210
#### Advanced Configuration Format
211
212
```javascript { .api }
213
interface AdvancedWatchConfig {
214
[scriptName: string]: {
215
patterns: string | string[]; // Glob patterns to watch
216
extensions?: string; // Comma-separated file extensions
217
ignore?: string | string[]; // Patterns to ignore
218
quiet?: boolean; // Hide script name in output
219
inherit?: boolean; // Inherit parent process stdio
220
legacyWatch?: boolean; // Enable nodemon legacy watch mode
221
delay?: number; // Restart delay in milliseconds
222
clearBuffer?: boolean; // Clear output buffer on restart
223
verbose?: boolean; // Enable nodemon verbose mode
224
runOnChangeOnly?: boolean; // Run only on file changes, not startup
225
silent?: boolean; // Enable nodemon silent mode
226
};
227
}
228
```
229
230
**Example:**
231
```json
232
{
233
"watch": {
234
"test": {
235
"patterns": ["src", "test"],
236
"extensions": "js,jsx,ts",
237
"ignore": ["build", "node_modules"],
238
"quiet": false,
239
"legacyWatch": false,
240
"delay": 1000,
241
"runOnChangeOnly": true
242
},
243
"build": {
244
"patterns": ["src/**/*.js"],
245
"extensions": "js,ts",
246
"clearBuffer": true,
247
"verbose": false,
248
"silent": false
249
},
250
"lint": {
251
"patterns": "src",
252
"extensions": "js,jsx,ts,tsx",
253
"inherit": true,
254
"ignore": "src/vendor"
255
}
256
}
257
}
258
```
259
260
**Real-world Configuration Examples:**
261
262
```json
263
{
264
"watch": {
265
"test:unit": {
266
"patterns": ["src", "test/unit"],
267
"extensions": "js,ts",
268
"runOnChangeOnly": true,
269
"quiet": true
270
},
271
"test:integration": {
272
"patterns": ["src", "test/integration"],
273
"extensions": "js,ts",
274
"delay": 2000
275
},
276
"build:dev": {
277
"patterns": "src",
278
"extensions": "js,jsx,ts,tsx",
279
"clearBuffer": true,
280
"ignore": ["src/**/*.test.js", "src/**/*.spec.js"]
281
}
282
},
283
"scripts": {
284
"test:unit": "jest --testPathPattern=unit",
285
"test:integration": "jest --testPathPattern=integration",
286
"build:dev": "webpack --mode=development"
287
}
288
}
289
```
290
291
#### Global Configuration
292
293
```javascript { .api }
294
interface WatchGlobalConfig {
295
setMaxListeners?: boolean; // Set max event listeners to avoid warnings
296
}
297
```
298
299
Applied to package.json as:
300
```json
301
{
302
"watchGlobalConfig": {
303
"setMaxListeners": true
304
}
305
}
306
```
307
308
309
## Error Handling
310
311
npm-watch provides several types of error handling:
312
313
- **Configuration Validation**: Verifies that `"watch"` configuration exists in package.json
314
- **Script Validation**: Ensures specified scripts exist in the `"scripts"` section
315
- **Option Conflicts**: Detects and reports conflicting options (e.g., verbose + silent)
316
- **Process Management**: Handles child process failures and cleanup
317
318
**Common Errors:**
319
- `No "watch" config in package.json`: Missing watch configuration
320
- `No such script "scriptName"`: Referenced script doesn't exist
321
- `Silent and Verbose can not both be on`: Conflicting configuration options
322
323
## Platform Support
324
325
npm-watch provides cross-platform compatibility:
326
327
- **Windows**: Uses `.cmd` variants of executables (npm.cmd, nodemon.cmd) and handles PATH differences
328
- **Unix-like Systems**: Uses standard executables and ANSI escape sequences
329
- **Shell Integration**: Automatically adds node_modules/.bin to PATH for script execution
330
331
## Dependencies
332
333
npm-watch relies on these key dependencies:
334
335
- **nodemon** (^3.0.1): Core file watching and process restarting functionality
336
- **through2** (^4.0.2): Stream transformation utilities for input/output handling
337
- **child_process.spawn**: Node.js built-in for process creation and management
338
339
## Troubleshooting
340
341
### Common Issues
342
343
**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:
344
345
```bash
346
npm install -g nodemon
347
```
348
349
**PATH 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.
350
351
**Memory Warnings**: If you see "MaxListenersExceededWarning" messages, add the global configuration:
352
353
```json
354
{
355
"watchGlobalConfig": {
356
"setMaxListeners": true
357
}
358
}
359
```