or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-watchpack

Wrapper library for directory and file watching with three-level architecture and optimized resource usage

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/watchpack@2.4.x

To install, run

npx @tessl/cli install tessl/npm-watchpack@2.4.0

0

# Watchpack

1

2

Watchpack is a wrapper library for directory and file watching that implements a three-level architecture to ensure only a single watcher exists per directory. It provides an event-driven API for monitoring file and directory changes with features including aggregated event handling, polling fallback for network paths, symlink handling options, and flexible ignore patterns. The library is designed for maximum efficiency through reference-counting, supports watching files that don't yet exist, and provides comprehensive time information tracking for all monitored resources.

3

4

## Package Information

5

6

- **Package Name**: watchpack

7

- **Package Type**: npm

8

- **Language**: JavaScript

9

- **Installation**: `npm install watchpack`

10

11

## Core Imports

12

13

```javascript

14

const Watchpack = require("watchpack");

15

```

16

17

For ES modules:

18

19

```javascript

20

import Watchpack from "watchpack";

21

```

22

23

## Basic Usage

24

25

```javascript

26

const Watchpack = require("watchpack");

27

28

// Create a new watcher instance

29

const wp = new Watchpack({

30

aggregateTimeout: 1000, // Fire aggregated event after 1000ms of no changes

31

poll: false, // Use native watching (set to true for network paths)

32

followSymlinks: false, // Don't follow symlinks

33

ignored: "**/.git" // Ignore git directories

34

});

35

36

// Set up event listeners

37

wp.on("change", function(filePath, mtime, explanation) {

38

console.log("File changed:", filePath);

39

console.log("Modified time:", mtime);

40

console.log("Detection method:", explanation);

41

});

42

43

wp.on("remove", function(filePath, explanation) {

44

console.log("File removed:", filePath);

45

console.log("Detection method:", explanation);

46

});

47

48

wp.on("aggregated", function(changes, removals) {

49

console.log("Changed files:", Array.from(changes));

50

console.log("Removed files:", Array.from(removals));

51

});

52

53

// Start watching

54

wp.watch({

55

files: ["/path/to/file.js", "/path/to/another.js"],

56

directories: ["/path/to/watch"],

57

missing: ["/path/that/might/be/created"],

58

startTime: Date.now() - 10000 // Start watching from 10 seconds ago

59

});

60

61

// Later: pause, resume, or close

62

wp.pause(); // Stop emitting events but keep watchers

63

wp.close(); // Stop watching completely

64

```

65

66

## Architecture

67

68

Watchpack implements a three-level architecture:

69

70

- **High-level API**: The `Watchpack` class provides the user-facing interface

71

- **DirectoryWatchers**: Managed by `WatcherManager`, ensures only one watcher per directory

72

- **Real Watchers**: Created by DirectoryWatcher, handles actual file system events

73

- **Reference Counting**: Automatic cleanup when watchers are no longer needed

74

- **Optimization**: Uses `reducePlan` to stay within system watcher limits

75

76

Key design principles:

77

- Files are never watched directly to keep watcher count low

78

- Watching can start in the past for consistency

79

- Symlinks are watched as symlinks, not followed by default

80

- Aggregated events reduce noise from rapid file changes

81

82

## Capabilities

83

84

### Main Watchpack Class

85

86

The primary interface for file and directory watching with event aggregation and resource optimization.

87

88

```javascript { .api }

89

/**

90

* Main watcher class providing high-level file/directory watching API

91

* @param {WatchpackOptions} options - Configuration options

92

*/

93

class Watchpack extends EventEmitter {

94

constructor(options);

95

}

96

97

/**

98

* Configuration options for Watchpack constructor

99

*/

100

interface WatchpackOptions {

101

/** Timeout in ms for aggregated events (default: 200) */

102

aggregateTimeout?: number;

103

/** Enable polling: true/false or polling interval in ms */

104

poll?: boolean | number;

105

/** Follow symlinks when watching (default: false) */

106

followSymlinks?: boolean;

107

/** Patterns/functions to ignore files/directories */

108

ignored?: string | string[] | RegExp | ((path: string) => boolean);

109

}

110

```

111

112

### Watching Operations

113

114

Core methods for starting, controlling, and stopping file system monitoring.

115

116

```javascript { .api }

117

/**

118

* Start watching specified files and directories

119

* @param {WatchOptions} options - What to watch

120

* @returns {void}

121

*/

122

watch(options);

123

124

/**

125

* Alternative signature for backward compatibility

126

* @param {string[]} files - Files to watch

127

* @param {string[]} directories - Directories to watch

128

* @param {number} [startTime] - Optional start time

129

* @returns {void}

130

*/

131

watch(files, directories, startTime);

132

133

/**

134

* Watch options interface

135

*/

136

interface WatchOptions {

137

/** Files to watch for content and existence changes */

138

files?: Iterable<string>;

139

/** Directories to watch for content changes (recursive) */

140

directories?: Iterable<string>;

141

/** Files/directories expected not to exist initially */

142

missing?: Iterable<string>;

143

/** Timestamp to start watching from */

144

startTime?: number;

145

}

146

147

/**

148

* Stop emitting events and close all watchers

149

* @returns {void}

150

*/

151

close();

152

153

/**

154

* Stop emitting events but keep watchers open for reuse

155

* @returns {void}

156

*/

157

pause();

158

```

159

160

### Time Information Access

161

162

Methods for accessing file modification times and metadata collected during watching.

163

164

```javascript { .api }

165

/**

166

* Get change times for all known files (deprecated)

167

* @returns {Object<string, number>} Object with file paths as keys, timestamps as values

168

*/

169

getTimes();

170

171

/**

172

* Get comprehensive time info entries for files and directories

173

* @returns {Map<string, TimeInfoEntry>} Map with paths and time info

174

*/

175

getTimeInfoEntries();

176

177

/**

178

* Collect time info entries into provided maps

179

* @param {Map<string, TimeInfoEntry>} fileTimestamps - Map for file time info

180

* @param {Map<string, TimeInfoEntry>} directoryTimestamps - Map for directory time info

181

* @returns {void}

182

*/

183

collectTimeInfoEntries(fileTimestamps, directoryTimestamps);

184

185

/**

186

* Time information entry structure

187

*/

188

interface TimeInfoEntry {

189

/** Safe time when all changes happened before this point */

190

safeTime: number;

191

/** File modification time (files only) */

192

timestamp?: number;

193

/** Timing accuracy information */

194

accuracy?: number;

195

}

196

```

197

198

### Aggregated Event Access

199

200

Methods for accessing batched file change information, useful when watching is paused.

201

202

```javascript { .api }

203

/**

204

* Get current aggregated changes and removals, clearing internal state

205

* @returns {AggregatedResult} Current changes and removals

206

*/

207

getAggregated();

208

209

/**

210

* Result structure for aggregated changes

211

*/

212

interface AggregatedResult {

213

/** Set of all changed file paths */

214

changes: Set<string>;

215

/** Set of all removed file paths */

216

removals: Set<string>;

217

}

218

```

219

220

### Events

221

222

File system events emitted during watching operations.

223

224

```javascript { .api }

225

/**

226

* Emitted when a file changes

227

* @event Watchpack#change

228

* @param {string} filePath - Path of changed file

229

* @param {number} mtime - Last modified time in milliseconds since Unix epoch

230

* @param {string} explanation - How change was detected (e.g., "watch", "poll", "initial scan")

231

*/

232

on('change', (filePath, mtime, explanation) => void);

233

234

/**

235

* Emitted when a file is removed

236

* @event Watchpack#remove

237

* @param {string} filePath - Path of removed file

238

* @param {string} explanation - How removal was detected (e.g., "watch", "poll")

239

*/

240

on('remove', (filePath, explanation) => void);

241

242

/**

243

* Emitted after aggregateTimeout with batched changes

244

* @event Watchpack#aggregated

245

* @param {Set<string>} changes - Set of all changed file paths

246

* @param {Set<string>} removals - Set of all removed file paths

247

*/

248

on('aggregated', (changes, removals) => void);

249

```

250

251

## Configuration Options

252

253

### Aggregate Timeout

254

255

Controls when the `aggregated` event fires after file changes stop:

256

257

```javascript

258

const wp = new Watchpack({

259

aggregateTimeout: 1000 // Wait 1000ms after last change

260

});

261

```

262

263

### Polling Mode

264

265

For network paths or when native watching fails:

266

267

```javascript

268

const wp = new Watchpack({

269

poll: true, // Use default polling interval

270

// or

271

poll: 5000 // Poll every 5 seconds

272

});

273

```

274

275

Can also be controlled via `WATCHPACK_POLLING` environment variable.

276

277

### Symlink Handling

278

279

Control how symlinks are handled:

280

281

```javascript

282

const wp = new Watchpack({

283

followSymlinks: true // Follow symlinks and watch both symlink and target

284

});

285

```

286

287

### Ignore Patterns

288

289

Flexible ignore patterns to exclude files and directories:

290

291

```javascript

292

const wp = new Watchpack({

293

ignored: "**/.git", // Glob pattern

294

// or

295

ignored: ["**/node_modules", "**/.git"], // Multiple patterns

296

// or

297

ignored: /\\.tmp$/, // Regular expression

298

// or

299

ignored: (path) => path.includes('temp') // Custom function

300

});

301

```

302

303

## Watch Types

304

305

### Files

306

307

Files are watched for content changes and existence. If a file doesn't exist initially, a `remove` event is emitted:

308

309

```javascript

310

wp.watch({

311

files: ["/path/to/file.js", "/path/to/config.json"]

312

});

313

```

314

315

### Directories

316

317

Directories are watched recursively for any changes to contained files and subdirectories:

318

319

```javascript

320

wp.watch({

321

directories: ["/path/to/src", "/path/to/assets"]

322

});

323

```

324

325

### Missing Files

326

327

Files or directories expected not to exist initially. No `remove` event is emitted if they don't exist:

328

329

```javascript

330

wp.watch({

331

missing: ["/path/that/might/be/created", "/future/directory"]

332

});

333

```

334

335

## Advanced Usage

336

337

### Starting Time

338

339

Watch for changes that occurred after a specific time:

340

341

```javascript

342

wp.watch({

343

files: ["file.js"],

344

startTime: Date.now() - 10000 // Changes in last 10 seconds

345

});

346

```

347

348

### Pausing and Resuming

349

350

Pause watching while keeping watcher instances for efficiency:

351

352

```javascript

353

wp.pause(); // Stop events but keep watchers

354

355

// Get changes that occurred while paused

356

const { changes, removals } = wp.getAggregated();

357

358

// Resume with new watch targets

359

wp.watch({ files: ["newfile.js"] });

360

```

361

362

### Time Information

363

364

Access detailed timing information for build tools:

365

366

```javascript

367

const timeInfo = wp.getTimeInfoEntries();

368

for (const [filePath, info] of timeInfo) {

369

console.log(`${filePath}: safeTime=${info.safeTime}, mtime=${info.timestamp}`);

370

}

371

```

372

373

## Error Handling

374

375

Watchpack handles common file system errors gracefully:

376

377

### Common Error Types

378

379

- **ENOENT (File not found)**: Triggers a `remove` event when a previously watched file is deleted

380

- **EPERM (Permission denied)**: Error is logged internally and watching continues for other files

381

- **EMFILE/ENFILE (Too many open files)**: Automatically optimizes watcher usage via `reducePlan` algorithm

382

- **Network path errors**: Automatically falls back to polling mode when native watching fails

383

384

### Error Handling Examples

385

386

```javascript

387

const wp = new Watchpack({

388

// Enable polling for network paths that might fail with native watching

389

poll: 1000,

390

// Ignore permission denied errors in certain directories

391

ignored: path => {

392

try {

393

fs.accessSync(path, fs.constants.R_OK);

394

return false;

395

} catch (err) {

396

return err.code === 'EPERM';

397

}

398

}

399

});

400

401

wp.on('change', (filePath, mtime, explanation) => {

402

// explanation provides context about how change was detected

403

if (explanation.includes('error')) {

404

console.warn(`Change detected with error context: ${explanation}`);

405

}

406

});

407

```

408

409

### Constructor Errors

410

411

The Watchpack constructor may throw errors for invalid options:

412

413

```javascript

414

try {

415

const wp = new Watchpack({

416

ignored: 123 // Invalid type - should be string, array, RegExp, or function

417

});

418

} catch (err) {

419

console.error('Invalid option for ignored:', err.message);

420

}

421

```

422

423

## Performance Considerations

424

425

- **Watcher Limits**: Automatically optimizes to stay within system limits (configurable via `WATCHPACK_WATCHER_LIMIT`)

426

- **Reference Counting**: Shares watchers between multiple watch targets

427

- **Batch Operations**: Use during initial setup for better performance

428

- **Recursive Watching**: Uses efficient recursive watchers on supported platforms (macOS, Windows)

429

430

## Environment Variables

431

432

Watchpack behavior can be controlled through several environment variables:

433

434

### `WATCHPACK_POLLING`

435

Controls polling mode when native file watching is unavailable or unreliable.

436

437

- **Values**: `"true"` or polling interval in milliseconds (e.g., `"1000"`)

438

- **Default**: `undefined` (use native watching)

439

- **Example**: `WATCHPACK_POLLING=5000 node app.js` (poll every 5 seconds)

440

441

### `WATCHPACK_WATCHER_LIMIT`

442

Sets the maximum number of watchers that can be created before optimization kicks in.

443

444

- **Values**: Positive integer

445

- **Default**: `10000` on Linux, `20` on macOS (due to system limitations)

446

- **Example**: `WATCHPACK_WATCHER_LIMIT=5000 node build.js`

447

448

### `WATCHPACK_RECURSIVE_WATCHER_LOGGING`

449

Enables detailed logging for recursive watcher operations, useful for debugging.

450

451

- **Values**: `"true"` to enable, any other value or unset to disable

452

- **Default**: `undefined` (disabled)

453

- **Example**: `WATCHPACK_RECURSIVE_WATCHER_LOGGING=true npm run dev`

454

455

### Usage Example

456

457

```bash

458

# Force polling mode with 2-second intervals and debug logging

459

WATCHPACK_POLLING=2000 WATCHPACK_RECURSIVE_WATCHER_LOGGING=true node build.js

460

461

# Limit watchers for resource-constrained environments

462

WATCHPACK_WATCHER_LIMIT=100 node server.js

463

```