CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-metro-file-map

File system crawling, watching and mapping library designed for Metro bundler ecosystem

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

error-handling.mddocs/

Error Handling

Specialized error classes for Haste conflicts, duplicate candidates, and other metro-file-map specific error conditions. These errors provide detailed information for debugging module resolution and file system issues.

Capabilities

HasteConflictsError

Error thrown when Haste module naming conflicts are detected and strict validation is enabled.

/**
 * Error for Haste module conflicts
 */
class HasteConflictsError extends Error {
  /**
   * Create error with conflict details
   * @param conflicts - Array of detected conflicts
   */
  constructor(conflicts: ReadonlyArray<HasteConflict>);
  
  /**
   * Get detailed error message with conflict information
   * @param pathsRelativeToRoot - Optional root path for relative paths in output
   * @returns Detailed error message string
   */
  getDetailedMessage(pathsRelativeToRoot?: string): string;
}

Usage Examples:

import { HasteConflictsError } from "metro-file-map";

try {
  // This will throw if conflicts detected and throwOnModuleCollision: true
  const fileMap = new FileMap({
    // ... options
    throwOnModuleCollision: true
  });
  
  await fileMap.build();
} catch (error) {
  if (error instanceof HasteConflictsError) {
    console.error('Haste conflicts detected:');
    console.error(error.getDetailedMessage(process.cwd()));
    
    // Example output:
    // Haste conflicts detected:
    // 
    // Duplicate module name "Button" found:
    //   - src/components/Button.js
    //   - src/ui/Button.js
    // 
    // Module "Header" has platform conflicts:
    //   - Platform "ios": src/Header.ios.js
    //   - Platform "ios": lib/Header.ios.js (shadows src/Header.js)
  } else {
    throw error;
  }
}

DuplicateHasteCandidatesError

Error thrown when multiple files declare the same Haste module name for a specific platform.

/**
 * Error for duplicate Haste module candidates
 */
class DuplicateHasteCandidatesError extends Error {
  /**
   * Create error for duplicate module candidates
   * @param name - Module name with duplicates
   * @param platform - Platform where duplicates exist
   * @param supportsNativePlatform - Whether native platform is supported
   * @param duplicatesSet - Set of duplicate file information
   */
  constructor(
    name: string,
    platform: string,
    supportsNativePlatform: boolean,
    duplicatesSet: DuplicatesSet
  );
}

Usage Examples:

import { DuplicateHasteCandidatesError } from "metro-file-map";

try {
  // Attempt to resolve module that has duplicates
  const modulePath = hasteMap.getModule('Button', 'ios');
} catch (error) {
  if (error instanceof DuplicateHasteCandidatesError) {
    console.error(`Multiple files found for module "${error.name}" on platform "${error.platform}"`);
    
    // Handle the duplicate by choosing one or prompting user
    const alternatives = Array.from(error.duplicatesSet.keys());
    console.log('Available options:');
    alternatives.forEach((path, index) => {
      console.log(`  ${index + 1}. ${path}`);
    });
  }
}

Error Context and Debugging

Errors provide rich context for debugging file system and module resolution issues.

/**
 * Conflict information included in errors
 */
interface HasteConflict {
  /** Module name with conflict */
  id: string;
  /** Platform where conflict occurs (null for all platforms) */
  platform: string | null;
  /** Absolute paths of conflicting files */
  absolutePaths: Array<string>;
  /** Type of conflict */
  type: 'duplicate' | 'shadowing';
}

/**
 * Duplicate candidates information
 */
type DuplicatesSet = Map<string, number>;

Usage Examples:

// Comprehensive error handling for FileMap operations
async function buildFileMapSafely(options) {
  try {
    const fileMap = new FileMap(options);
    const result = await fileMap.build();
    
    // Check for conflicts even if not throwing
    const conflicts = result.hasteMap.computeConflicts();
    if (conflicts.length > 0) {
      console.warn(`Warning: ${conflicts.length} Haste conflicts detected`);
      conflicts.forEach(conflict => {
        console.warn(`  ${conflict.type}: ${conflict.id} (${conflict.platform || 'all platforms'})`);
        conflict.absolutePaths.forEach(path => {
          console.warn(`    - ${path}`);
        });
      });
    }
    
    return result;
  } catch (error) {
    if (error instanceof HasteConflictsError) {
      console.error('❌ Haste module conflicts:');
      console.error(error.getDetailedMessage());
      process.exit(1);
    } else if (error instanceof DuplicateHasteCandidatesError) {
      console.error('❌ Duplicate module candidates detected');
      console.error(error.message);
      process.exit(1);
    } else {
      console.error('❌ Unexpected error during file mapping:');
      console.error(error);
      throw error;
    }
  }
}

// Usage
const result = await buildFileMapSafely({
  extensions: ['.js', '.ts'],
  platforms: ['ios', 'android'],
  retainAllFiles: false,
  rootDir: process.cwd(),
  roots: ['./src'],
  maxWorkers: 4,
  healthCheck: { enabled: false, interval: 30000, timeout: 5000, filePrefix: 'test' },
  throwOnModuleCollision: true
});

Conflict Resolution Strategies

Handle conflicts programmatically based on error information.

Usage Examples:

// Automatic conflict resolution
class ConflictResolver {
  static resolveHasteConflicts(conflicts) {
    const resolutions = new Map();
    
    conflicts.forEach(conflict => {
      switch (conflict.type) {
        case 'duplicate':
          // Choose file with shortest path (closest to root)
          const shortestPath = conflict.absolutePaths.reduce((shortest, current) => 
            current.length < shortest.length ? current : shortest
          );
          resolutions.set(conflict.id, shortestPath);
          console.log(`Resolved duplicate "${conflict.id}" -> ${shortestPath}`);
          break;
          
        case 'shadowing':
          // Prefer platform-specific over generic
          const platformSpecific = conflict.absolutePaths.find(path => 
            path.includes(`.${conflict.platform}.`)
          );
          if (platformSpecific) {
            resolutions.set(conflict.id, platformSpecific);
            console.log(`Resolved shadowing "${conflict.id}" -> ${platformSpecific}`);
          }
          break;
      }
    });
    
    return resolutions;
  }
  
  static async buildWithConflictResolution(options) {
    try {
      return await buildFileMapSafely({ ...options, throwOnModuleCollision: true });
    } catch (error) {
      if (error instanceof HasteConflictsError) {
        console.log('Attempting automatic conflict resolution...');
        
        const resolutions = this.resolveHasteConflicts(error.conflicts);
        
        // Retry with conflict resolution (implementation would need custom plugin)
        return await buildFileMapSafely({
          ...options,
          throwOnModuleCollision: false,
          conflictResolutions: resolutions
        });
      }
      throw error;
    }
  }
}

Error Prevention

Strategies for preventing common errors through configuration.

Usage Examples:

// Configuration to prevent common errors
const robustFileMapOptions = {
  extensions: ['.js', '.jsx', '.ts', '.tsx'],
  platforms: ['ios', 'android', 'native', 'web'],
  retainAllFiles: false,
  rootDir: process.cwd(),
  roots: ['./src'],
  maxWorkers: require('os').cpus().length,
  healthCheck: { enabled: false, interval: 30000, timeout: 5000, filePrefix: 'test' },
  
  // Error prevention settings
  throwOnModuleCollision: false,        // Don't throw on conflicts
  ignorePattern: /node_modules|\.git/,  // Ignore problematic directories
  enableHastePackages: false,           // Disable if causing conflicts
  
  // Custom conflict-aware plugin
  plugins: [
    new ConflictAwareHastePlugin({
      enableHastePackages: false,
      platforms: new Set(['ios', 'android', 'native', 'web']),
      rootDir: process.cwd(),
      failValidationOnConflicts: false,
      autoResolveConflicts: true
    })
  ]
};

// Build with error recovery
async function buildWithRetry(options, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await buildFileMapSafely(options);
    } catch (error) {
      console.log(`Attempt ${attempt}/${maxRetries} failed:`, error.message);
      
      if (attempt === maxRetries) {
        console.error('All retry attempts failed');
        throw error;
      }
      
      // Modify options for retry (e.g., disable features causing errors)
      if (error instanceof HasteConflictsError) {
        options.throwOnModuleCollision = false;
        options.enableHastePackages = false;
      }
    }
  }
}

Types

interface HasteConflict {
  id: string;
  platform: string | null;
  absolutePaths: Array<string>;
  type: 'duplicate' | 'shadowing';
}

type DuplicatesSet = Map<string, number>;

type DuplicatesIndex = Map<string, Map<string, DuplicatesSet>>;

interface ErrorWithConflicts extends Error {
  conflicts: ReadonlyArray<HasteConflict>;
}

docs

cache-management.md

error-handling.md

file-map-core.md

file-system.md

haste-system.md

index.md

plugin-system.md

tile.json