0
# Error Handling
1
2
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.
3
4
## Capabilities
5
6
### HasteConflictsError
7
8
Error thrown when Haste module naming conflicts are detected and strict validation is enabled.
9
10
```javascript { .api }
11
/**
12
* Error for Haste module conflicts
13
*/
14
class HasteConflictsError extends Error {
15
/**
16
* Create error with conflict details
17
* @param conflicts - Array of detected conflicts
18
*/
19
constructor(conflicts: ReadonlyArray<HasteConflict>);
20
21
/**
22
* Get detailed error message with conflict information
23
* @param pathsRelativeToRoot - Optional root path for relative paths in output
24
* @returns Detailed error message string
25
*/
26
getDetailedMessage(pathsRelativeToRoot?: string): string;
27
}
28
```
29
30
**Usage Examples:**
31
32
```javascript
33
import { HasteConflictsError } from "metro-file-map";
34
35
try {
36
// This will throw if conflicts detected and throwOnModuleCollision: true
37
const fileMap = new FileMap({
38
// ... options
39
throwOnModuleCollision: true
40
});
41
42
await fileMap.build();
43
} catch (error) {
44
if (error instanceof HasteConflictsError) {
45
console.error('Haste conflicts detected:');
46
console.error(error.getDetailedMessage(process.cwd()));
47
48
// Example output:
49
// Haste conflicts detected:
50
//
51
// Duplicate module name "Button" found:
52
// - src/components/Button.js
53
// - src/ui/Button.js
54
//
55
// Module "Header" has platform conflicts:
56
// - Platform "ios": src/Header.ios.js
57
// - Platform "ios": lib/Header.ios.js (shadows src/Header.js)
58
} else {
59
throw error;
60
}
61
}
62
```
63
64
### DuplicateHasteCandidatesError
65
66
Error thrown when multiple files declare the same Haste module name for a specific platform.
67
68
```javascript { .api }
69
/**
70
* Error for duplicate Haste module candidates
71
*/
72
class DuplicateHasteCandidatesError extends Error {
73
/**
74
* Create error for duplicate module candidates
75
* @param name - Module name with duplicates
76
* @param platform - Platform where duplicates exist
77
* @param supportsNativePlatform - Whether native platform is supported
78
* @param duplicatesSet - Set of duplicate file information
79
*/
80
constructor(
81
name: string,
82
platform: string,
83
supportsNativePlatform: boolean,
84
duplicatesSet: DuplicatesSet
85
);
86
}
87
```
88
89
**Usage Examples:**
90
91
```javascript
92
import { DuplicateHasteCandidatesError } from "metro-file-map";
93
94
try {
95
// Attempt to resolve module that has duplicates
96
const modulePath = hasteMap.getModule('Button', 'ios');
97
} catch (error) {
98
if (error instanceof DuplicateHasteCandidatesError) {
99
console.error(`Multiple files found for module "${error.name}" on platform "${error.platform}"`);
100
101
// Handle the duplicate by choosing one or prompting user
102
const alternatives = Array.from(error.duplicatesSet.keys());
103
console.log('Available options:');
104
alternatives.forEach((path, index) => {
105
console.log(` ${index + 1}. ${path}`);
106
});
107
}
108
}
109
```
110
111
### Error Context and Debugging
112
113
Errors provide rich context for debugging file system and module resolution issues.
114
115
```javascript { .api }
116
/**
117
* Conflict information included in errors
118
*/
119
interface HasteConflict {
120
/** Module name with conflict */
121
id: string;
122
/** Platform where conflict occurs (null for all platforms) */
123
platform: string | null;
124
/** Absolute paths of conflicting files */
125
absolutePaths: Array<string>;
126
/** Type of conflict */
127
type: 'duplicate' | 'shadowing';
128
}
129
130
/**
131
* Duplicate candidates information
132
*/
133
type DuplicatesSet = Map<string, number>;
134
```
135
136
**Usage Examples:**
137
138
```javascript
139
// Comprehensive error handling for FileMap operations
140
async function buildFileMapSafely(options) {
141
try {
142
const fileMap = new FileMap(options);
143
const result = await fileMap.build();
144
145
// Check for conflicts even if not throwing
146
const conflicts = result.hasteMap.computeConflicts();
147
if (conflicts.length > 0) {
148
console.warn(`Warning: ${conflicts.length} Haste conflicts detected`);
149
conflicts.forEach(conflict => {
150
console.warn(` ${conflict.type}: ${conflict.id} (${conflict.platform || 'all platforms'})`);
151
conflict.absolutePaths.forEach(path => {
152
console.warn(` - ${path}`);
153
});
154
});
155
}
156
157
return result;
158
} catch (error) {
159
if (error instanceof HasteConflictsError) {
160
console.error('❌ Haste module conflicts:');
161
console.error(error.getDetailedMessage());
162
process.exit(1);
163
} else if (error instanceof DuplicateHasteCandidatesError) {
164
console.error('❌ Duplicate module candidates detected');
165
console.error(error.message);
166
process.exit(1);
167
} else {
168
console.error('❌ Unexpected error during file mapping:');
169
console.error(error);
170
throw error;
171
}
172
}
173
}
174
175
// Usage
176
const result = await buildFileMapSafely({
177
extensions: ['.js', '.ts'],
178
platforms: ['ios', 'android'],
179
retainAllFiles: false,
180
rootDir: process.cwd(),
181
roots: ['./src'],
182
maxWorkers: 4,
183
healthCheck: { enabled: false, interval: 30000, timeout: 5000, filePrefix: 'test' },
184
throwOnModuleCollision: true
185
});
186
```
187
188
### Conflict Resolution Strategies
189
190
Handle conflicts programmatically based on error information.
191
192
**Usage Examples:**
193
194
```javascript
195
// Automatic conflict resolution
196
class ConflictResolver {
197
static resolveHasteConflicts(conflicts) {
198
const resolutions = new Map();
199
200
conflicts.forEach(conflict => {
201
switch (conflict.type) {
202
case 'duplicate':
203
// Choose file with shortest path (closest to root)
204
const shortestPath = conflict.absolutePaths.reduce((shortest, current) =>
205
current.length < shortest.length ? current : shortest
206
);
207
resolutions.set(conflict.id, shortestPath);
208
console.log(`Resolved duplicate "${conflict.id}" -> ${shortestPath}`);
209
break;
210
211
case 'shadowing':
212
// Prefer platform-specific over generic
213
const platformSpecific = conflict.absolutePaths.find(path =>
214
path.includes(`.${conflict.platform}.`)
215
);
216
if (platformSpecific) {
217
resolutions.set(conflict.id, platformSpecific);
218
console.log(`Resolved shadowing "${conflict.id}" -> ${platformSpecific}`);
219
}
220
break;
221
}
222
});
223
224
return resolutions;
225
}
226
227
static async buildWithConflictResolution(options) {
228
try {
229
return await buildFileMapSafely({ ...options, throwOnModuleCollision: true });
230
} catch (error) {
231
if (error instanceof HasteConflictsError) {
232
console.log('Attempting automatic conflict resolution...');
233
234
const resolutions = this.resolveHasteConflicts(error.conflicts);
235
236
// Retry with conflict resolution (implementation would need custom plugin)
237
return await buildFileMapSafely({
238
...options,
239
throwOnModuleCollision: false,
240
conflictResolutions: resolutions
241
});
242
}
243
throw error;
244
}
245
}
246
}
247
```
248
249
### Error Prevention
250
251
Strategies for preventing common errors through configuration.
252
253
**Usage Examples:**
254
255
```javascript
256
// Configuration to prevent common errors
257
const robustFileMapOptions = {
258
extensions: ['.js', '.jsx', '.ts', '.tsx'],
259
platforms: ['ios', 'android', 'native', 'web'],
260
retainAllFiles: false,
261
rootDir: process.cwd(),
262
roots: ['./src'],
263
maxWorkers: require('os').cpus().length,
264
healthCheck: { enabled: false, interval: 30000, timeout: 5000, filePrefix: 'test' },
265
266
// Error prevention settings
267
throwOnModuleCollision: false, // Don't throw on conflicts
268
ignorePattern: /node_modules|\.git/, // Ignore problematic directories
269
enableHastePackages: false, // Disable if causing conflicts
270
271
// Custom conflict-aware plugin
272
plugins: [
273
new ConflictAwareHastePlugin({
274
enableHastePackages: false,
275
platforms: new Set(['ios', 'android', 'native', 'web']),
276
rootDir: process.cwd(),
277
failValidationOnConflicts: false,
278
autoResolveConflicts: true
279
})
280
]
281
};
282
283
// Build with error recovery
284
async function buildWithRetry(options, maxRetries = 3) {
285
for (let attempt = 1; attempt <= maxRetries; attempt++) {
286
try {
287
return await buildFileMapSafely(options);
288
} catch (error) {
289
console.log(`Attempt ${attempt}/${maxRetries} failed:`, error.message);
290
291
if (attempt === maxRetries) {
292
console.error('All retry attempts failed');
293
throw error;
294
}
295
296
// Modify options for retry (e.g., disable features causing errors)
297
if (error instanceof HasteConflictsError) {
298
options.throwOnModuleCollision = false;
299
options.enableHastePackages = false;
300
}
301
}
302
}
303
}
304
```
305
306
## Types
307
308
```javascript { .api }
309
interface HasteConflict {
310
id: string;
311
platform: string | null;
312
absolutePaths: Array<string>;
313
type: 'duplicate' | 'shadowing';
314
}
315
316
type DuplicatesSet = Map<string, number>;
317
318
type DuplicatesIndex = Map<string, Map<string, DuplicatesSet>>;
319
320
interface ErrorWithConflicts extends Error {
321
conflicts: ReadonlyArray<HasteConflict>;
322
}
323
```