UNPKG

@jantimon/glob-watch

Version:

High-performance file watcher with multiple backend options

264 lines (187 loc) 7.1 kB
# @jantimon/glob-watch [![npm version](https://badge.fury.io/js/%40jantimon%2Fglob-watch.svg)](https://badge.fury.io/js/%40jantimon%2Fglob-watch) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) A high-performance, flexible file watching utility with smart backend selection ## Features - **Multiple watching strategies:** - Uses `fb-watchman` for maximum performance when available - Falls back to Node.js native `fs.watch` with glob matching - Supports one-time scanning with `fast-glob` - **Modern & developer-friendly:** - TypeScript native (no compilation needed for Node.js 22+, Bun, and Deno) - Consistent API across all watching backends - Minimal dependencies ## Motivation Traditional file watchers often force a trade-off between performance, flexibility, and consistency. With @jantimon/glob-watch, you get: - **Optimal performance** by leveraging Facebook's Watchman when available - **Environment adaptability** with automatic fallbacks for CI environments or platforms like Stackblitz - **Unified API** regardless of which backend is handling the watching - **Precise control** over which file information is collected - **Dual-purpose functionality** with both watching (`watch`) and one-time file discovery (`findFiles`) ## Installation ```bash npm install @jantimon/glob-watch ``` Recommended - for optimal watcher performance, consider installing the optional Watchman dependency: ```bash npm install fb-watchman ``` ## Basic Usage ### Watch for Changes ```typescript import { watch } from "@jantimon/glob-watch"; // Start watching files const destroy = await watch("src/**/*.ts", (changes) => { console.log("Added files:", changes.added); console.log("Changed files:", changes.changed); console.log("Deleted files:", changes.deleted); }); // Stop watching when done destroy(); ``` ### Find Files (One-time Operation) ```typescript import { findFiles } from "@jantimon/glob-watch"; // Perfect for build scripts and CI environments const files = await findFiles("src/**/*.ts"); console.log(`Found ${files.length} TypeScript files`); ``` ## API ### watch(patterns, callback, [options]) Starts watching files that match the provided glob pattern(s). #### Parameters - `patterns`: `string | string[]` - Glob pattern(s) to match - `callback`: `(changes: FileChanges) => void | Promise<void>` - Callback function that receives file changes - `options`: `WatchOptions` (optional) - Configuration options #### Returns - `Promise<DestroyFunction>` - A function to stop watching ### findFiles(patterns, [options]) Performs a one-time scan for files matching the provided glob pattern(s). #### Parameters - `patterns`: `string | string[]` - Glob pattern(s) to match - `options`: `WatchOptions` (optional) - Configuration options #### Returns - `Promise<string[]>` - Array of matching file paths ### FileChanges An object containing maps of file changes: ```typescript interface FileChanges { added: Map<string, FileInfo>; // New files changed: Map<string, FileInfo>; // Modified files deleted: Map<string, FileInfo>; // Removed files } ``` ### FileInfo Information about a file: ```typescript interface FileInfo { // Base fields that will always be available name: string; // Filename path: string; // Path to the file (relative or absolute based on options) // Optional fields that can be requested exists?: boolean; type?: string; // File type size?: number; // File size in bytes mtime?: number; // Modification time } ``` ## Options Both `watch` and `findFiles` accept the same options object: ```typescript { // Watcher backend selection (for watch function) mode?: "watchman" | "native" | "fast-glob"; // Which information to include in FileInfo objects fields?: Array<"type" | "size" | "mtime">; // Path handling absolute?: boolean; // Return absolute paths (default: false) cwd?: string; // Base directory for relative paths (default: process.cwd()) // File filtering onlyDirectories?: boolean; // Return only directories (default: false) onlyFiles?: boolean; // Return only files (default: true) // Pattern matching dot?: boolean; // Match files starting with . (default: false) } ``` ## Watcher Backends ### watchman (default) Uses Facebook's [Watchman](https://facebook.github.io/watchman/) through the `fb-watchman` npm package. Offers the best performance and scalability for large projects. ### native Uses Node.js built-in `fs.watch` API combined with `fast-glob` for initial file discovery and pattern matching. Available everywhere without external Watchman dependency. Used as default fallback for `watch` if `fb-watchman` is not installed. ### fast-glob Performs a single scan using the `fast-glob` package and immediately returns. Useful for one-time operations when you don't need continuous watching. Used as default fallback for `findFiles` if `fb-watchman` is not installed. ## Examples ### Watch Multiple Patterns ```typescript const destroy = await watch(["src/**/*.ts", "test/**/*.ts"], (changes) => { // Handle changes }); ``` ### Request Specific File Information ```typescript const destroy = await watch( "**/*.js", (changes) => { for (const [path, info] of changes.added) { console.log( `New file ${path} with size ${info.size} bytes, modified at ${new Date(info.mtime)}`, ); } }, { fields: ["size", "mtime", "type"], }, ); ``` ### One-time Scan There are two ways to perform a one-time scan: ```typescript // Method 1: Using findFiles const files = await findFiles("**/*.json"); console.log(`Found ${files.length} JSON files`); // Method 2: Using watch with fast-glob mode const destroy = await watch("**/*.json", (changes) => { // This callback runs once with all matching files in changes.added console.log(`Found ${changes.added.size} JSON files`); }); // Stop watching immediately after the initial scan destroy(); ``` ### Working with Absolute Paths ```typescript const destroy = await watch( "src/**/*.ts", (changes) => { // All paths in changes will be absolute for (const path of changes.added.keys()) { console.log(`Found: ${path}`); } }, { absolute: true }, ); ``` ### Focus on Directories Only ```typescript const destroy = await watch( "src/**/*", (changes) => { // Only directories will be reported }, { onlyDirectories: true, onlyFiles: false, }, ); ``` ## Dependencies - **Required peer dependency:** - `fast-glob`: For pattern matching in both `native` and `fast-glob` modes - **Optional dependency:** - `fb-watchman`: For using the high-performance Watchman backend If `fb-watchman` isn't available, `@jantimon/glob-watch` will automatically fallback to `native` mode. ## Compatibility - **Node.js:** v22.0.0 or higher recommended for TypeScript native support - **Other runtimes:** Compatible with Bun and Deno For optimal testing, ensure Watchman is installed on your system. ## License [MIT](https://opensource.org/licenses/MIT)