CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jest-haste-map

A module used by Jest to create a fast lookup of files in a project

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

file-system-watching.mddocs/

File System Watching

Real-time file system monitoring for interactive development tools, providing change events and automatic map updates. The watching system supports multiple backends including Watchman, FSEvents, and Node.js fs.watch, with intelligent change detection and batching.

Capabilities

Watch Mode Configuration

Configure file system watching when creating the haste map.

/**
 * Enable file system watching in HasteMap options
 */
interface WatchOptions extends Options {
  /** Enable file system watching, defaults to false */
  watch?: boolean;
  /** Use Watchman for file system monitoring, defaults to true */
  useWatchman?: boolean;
  /** Whether to throw errors on module name collisions in watch mode, defaults to false */
  throwOnModuleCollision?: boolean;
}

Change Event Handling

Subscribe to file system changes through the EventEmitter interface.

/**
 * HasteMap extends EventEmitter to provide change notifications
 */
interface IHasteMap extends EventEmitter {
  /**
   * Subscribe to file system change events
   * @param eventType - Event type, currently only 'change' is supported
   * @param handler - Function to handle change events
   */
  on(eventType: 'change', handler: (event: ChangeEvent) => void): void;
}

/**
 * Change event data structure providing access to changed files and updated maps
 */
interface ChangeEvent {
  /** Queue of file system events that triggered this change */
  eventsQueue: EventsQueue;
  /** Updated HasteFS instance with current file system state */
  hasteFS: IHasteFS;
  /** Updated ModuleMap instance with current module mappings */
  moduleMap: IModuleMap;
}

/**
 * Queue of individual file system events
 */
interface EventsQueue extends Array<{
  /** Absolute path to the changed file */
  filePath: string;
  /** File statistics, undefined for deleted files */
  stat: Stats | undefined;
  /** Type of change: 'add', 'change', or 'unlink' */
  type: string;
}> {}

Lifecycle Management

Methods for managing the watching lifecycle.

/**
 * Stop file system watching and cleanup resources
 * @returns Promise that resolves when all watchers are closed
 */
end(): Promise<void>;

Usage Examples:

import HasteMap from "jest-haste-map";

// Create haste map with watching enabled
const hasteMap = await HasteMap.create({
  id: "my-project",
  extensions: ["js", "ts", "jsx", "tsx"],
  maxWorkers: 4,
  platforms: ["ios", "android"],
  roots: ["/src"],
  retainAllFiles: true,
  rootDir: "/project/root",
  watch: true,              // Enable watching
  useWatchman: true,        // Prefer Watchman if available
});

// Build initial map
const {hasteFS, moduleMap} = await hasteMap.build();

// Set up change handler
hasteMap.on('change', (event) => {
  console.log(`Detected ${event.eventsQueue.length} file system changes:`);
  
  for (const fileEvent of event.eventsQueue) {
    console.log(`  ${fileEvent.type}: ${fileEvent.filePath}`);
    
    if (fileEvent.stat) {
      console.log(`    Size: ${fileEvent.stat.size} bytes`);
      console.log(`    Modified: ${fileEvent.stat.mtime}`);
    }
  }
  
  // Access updated file system
  const updatedFiles = event.hasteFS.getAllFiles();
  console.log(`Total files now: ${updatedFiles.length}`);
  
  // Check specific module resolution
  const appPath = event.moduleMap.getModule('App');
  if (appPath) {
    console.log(`App module resolved to: ${appPath}`);
    
    // Get updated dependencies
    const appDeps = event.hasteFS.getDependencies(appPath);
    console.log(`App dependencies: ${appDeps?.length || 0}`);
  }
});

// In a real application, keep the process running
// The change handler will be called whenever files change
console.log("Watching for file changes...");

// Clean up when shutting down
process.on('SIGINT', async () => {
  console.log("Shutting down file watcher...");
  await hasteMap.end();
  process.exit(0);
});

Watcher Backend Selection

The watching system automatically selects the best available backend:

  1. Watchman (preferred): Facebook's file watching service, optimal for large projects
  2. FSEvents (macOS): Native macOS file system events, efficient for Mac development
  3. NodeWatcher: Cross-platform fallback using Node.js fs.watch
// The backend selection is automatic, but you can control preferences
const hasteMap = await HasteMap.create({
  id: "my-project",
  extensions: ["js", "ts"],
  maxWorkers: 4,
  platforms: [],
  roots: ["/src"],
  retainAllFiles: true,
  rootDir: "/project/root",
  watch: true,
  useWatchman: false,  // Disable Watchman, will use FSEvents or NodeWatcher
});

Change Detection and Batching

The watching system includes intelligent change detection and batching:

  • Duplicate Detection: Filters out duplicate events for the same file
  • Change Batching: Groups rapid changes into single events (30ms intervals)
  • Mtime Checking: Only processes files that have actually changed
  • Incremental Updates: Only re-processes files that have changed, not the entire project
// Example of handling batched changes
hasteMap.on('change', (event) => {
  const changedFiles = event.eventsQueue.filter(e => e.type === 'change');
  const addedFiles = event.eventsQueue.filter(e => e.type === 'add');
  const deletedFiles = event.eventsQueue.filter(e => e.type === 'unlink');
  
  console.log(`Batch contains: ${changedFiles.length} changed, ${addedFiles.length} added, ${deletedFiles.length} deleted`);
  
  // Process different types of changes
  for (const added of addedFiles) {
    console.log(`New file detected: ${added.filePath}`);
    
    // Check if it's a new module
    const moduleName = event.hasteFS.getModuleName(added.filePath);
    if (moduleName) {
      console.log(`  New module available: ${moduleName}`);
    }
  }
  
  for (const deleted of deletedFiles) {
    console.log(`File deleted: ${deleted.filePath}`);
    
    // Note: deleted files won't exist in the updated hasteFS
    const stillExists = event.hasteFS.exists(deleted.filePath);
    console.log(`  Still in map: ${stillExists}`); // Should be false
  }
});

Integration with Development Tools

File system watching is particularly useful for development tools:

// Development server integration
class DevServer {
  private hasteMap: IHasteMap;
  private clients: WebSocket[] = [];
  
  async start() {
    this.hasteMap = await HasteMap.create({
      id: "dev-server",
      extensions: ["js", "ts", "jsx", "tsx", "css", "json"],
      maxWorkers: 4,
      platforms: [],
      roots: ["/src", "/public"],
      retainAllFiles: true,
      rootDir: process.cwd(),
      watch: true,
    });
    
    await this.hasteMap.build();
    
    // Watch for changes and notify clients
    this.hasteMap.on('change', (event) => {
      const changes = event.eventsQueue.map(e => ({
        type: e.type,
        path: e.filePath,
        size: e.stat?.size,
      }));
      
      // Notify all connected clients
      this.clients.forEach(client => {
        client.send(JSON.stringify({
          type: 'file-change',
          changes,
        }));
      });
      
      // Rebuild if package.json changed
      const packageJsonChanged = event.eventsQueue.some(
        e => e.filePath.endsWith('package.json')
      );
      
      if (packageJsonChanged) {
        console.log('package.json changed, triggering rebuild...');
        this.rebuild();
      }
    });
  }
  
  async stop() {
    await this.hasteMap.end();
  }
  
  private rebuild() {
    // Restart the server or rebuild assets
  }
}

Error Handling in Watch Mode

Watch mode automatically handles many error conditions:

  • File Access Errors: Files that become inaccessible are automatically removed from the map
  • Permission Errors: Gracefully handled, with warnings logged
  • Watcher Failures: Automatic fallback to different watcher backends
  • Module Collisions: In watch mode, collisions generate warnings instead of errors by default
// Watch mode error handling
const hasteMap = await HasteMap.create({
  id: "my-project",
  extensions: ["js", "ts"],
  maxWorkers: 4,
  platforms: [],
  roots: ["/src"],
  retainAllFiles: true,
  rootDir: "/project/root",
  watch: true,
  throwOnModuleCollision: false, // Warn instead of throw in watch mode
  console: {
    // Custom console for handling warnings
    log: console.log,
    warn: (message) => {
      console.warn(`[Haste Warning] ${message}`);
    },
    error: console.error,
  },
});

hasteMap.on('change', (event) => {
  // Changes are automatically applied even if some files had errors
  console.log('Map updated successfully despite any individual file errors');
});

docs

file-system-mapping.md

file-system-watching.md

index.md

module-resolution.md

virtual-file-system.md

tile.json