File system crawling, watching and mapping library designed for Metro bundler ecosystem
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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;
}
}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}`);
});
}
}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
});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;
}
}
}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;
}
}
}
}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>;
}