0
# Haste Module System
1
2
Haste module resolution and conflict detection system for fast module lookup by name. The Haste system enables importing modules by their declared name rather than relative paths, similar to Node.js resolution but optimized for Metro bundler workflows.
3
4
## Capabilities
5
6
### Module Resolution
7
8
Resolve modules and packages by name with platform-specific support.
9
10
```javascript { .api }
11
/**
12
* Get module path by name
13
* @param name - Module name to resolve
14
* @param platform - Platform identifier (e.g., 'ios', 'android', 'web')
15
* @param supportsNativePlatform - Whether to support 'native' platform fallback
16
* @param type - Module type (MODULE=0, PACKAGE=1)
17
* @returns Resolved module path or null if not found
18
*/
19
getModule(
20
name: string,
21
platform?: string,
22
supportsNativePlatform?: boolean,
23
type?: 0 | 1
24
): string | null;
25
26
/**
27
* Get package path by name
28
* @param name - Package name to resolve
29
* @param platform - Platform identifier
30
* @param supportsNativePlatform - Whether to support 'native' platform fallback
31
* @returns Resolved package path or null if not found
32
*/
33
getPackage(
34
name: string,
35
platform?: string,
36
supportsNativePlatform?: boolean
37
): string | null;
38
```
39
40
**Usage Examples:**
41
42
```javascript
43
const { hasteMap } = await fileMap.build();
44
45
// Resolve module by name
46
const buttonPath = hasteMap.getModule('Button');
47
console.log('Button module:', buttonPath);
48
// Example output: "src/components/Button.js"
49
50
// Platform-specific resolution
51
const iosButton = hasteMap.getModule('Button', 'ios');
52
const androidButton = hasteMap.getModule('Button', 'android');
53
54
console.log('iOS Button:', iosButton); // "src/components/Button.ios.js"
55
console.log('Android Button:', androidButton); // "src/components/Button.android.js"
56
57
// Package resolution
58
const utilsPackage = hasteMap.getPackage('utils');
59
console.log('Utils package:', utilsPackage);
60
// Example output: "src/utils/package.json"
61
62
// Native platform fallback
63
const nativeModule = hasteMap.getModule('NativeHelper', 'ios', true);
64
// Will try: NativeHelper.ios.js -> NativeHelper.native.js -> NativeHelper.js
65
```
66
67
### Platform Resolution Strategy
68
69
The Haste system follows a specific resolution order for platform-specific modules:
70
71
1. **Exact platform match**: `ModuleName.platform.js`
72
2. **Native fallback** (if enabled): `ModuleName.native.js`
73
3. **Generic fallback**: `ModuleName.js`
74
75
```javascript { .api }
76
// Example resolution for getModule('Button', 'ios', true):
77
// 1. Button.ios.js
78
// 2. Button.native.js (if supportsNativePlatform=true)
79
// 3. Button.js
80
81
// File structure example:
82
// src/components/
83
// Button.js <- Generic implementation
84
// Button.ios.js <- iOS-specific
85
// Button.android.js <- Android-specific
86
// Button.native.js <- Native fallback (for ios/android)
87
88
const genericButton = hasteMap.getModule('Button'); // -> Button.js
89
const iosButton = hasteMap.getModule('Button', 'ios'); // -> Button.ios.js
90
const webButton = hasteMap.getModule('Button', 'web'); // -> Button.js (fallback)
91
```
92
93
### Conflict Detection
94
95
Detect and analyze Haste module naming conflicts.
96
97
```javascript { .api }
98
/**
99
* Compute all Haste conflicts in the module map
100
* @returns Array of conflict descriptions
101
*/
102
computeConflicts(): Array<HasteConflict>;
103
104
interface HasteConflict {
105
/** Module name with conflict */
106
id: string;
107
/** Platform where conflict occurs (null for all platforms) */
108
platform: string | null;
109
/** Absolute paths of conflicting files */
110
absolutePaths: Array<string>;
111
/** Type of conflict */
112
type: 'duplicate' | 'shadowing';
113
}
114
```
115
116
**Usage Examples:**
117
118
```javascript
119
// Check for module conflicts
120
const conflicts = hasteMap.computeConflicts();
121
122
if (conflicts.length > 0) {
123
console.log(`Found ${conflicts.length} Haste conflicts:`);
124
125
conflicts.forEach(conflict => {
126
console.log(`\nConflict: ${conflict.id}`);
127
console.log(`Platform: ${conflict.platform || 'all'}`);
128
console.log(`Type: ${conflict.type}`);
129
console.log('Paths:');
130
conflict.absolutePaths.forEach(path => {
131
console.log(` - ${path}`);
132
});
133
});
134
} else {
135
console.log('No Haste conflicts detected');
136
}
137
138
// Example conflict scenarios:
139
140
// Duplicate conflict - same module name in different locations:
141
// src/components/Button.js (exports @providesModule Button)
142
// src/ui/Button.js (exports @providesModule Button)
143
144
// Shadowing conflict - platform-specific shadowing generic:
145
// src/Button.js (generic)
146
// lib/Button.ios.js (shadows generic for iOS)
147
```
148
149
### HastePlugin Integration
150
151
The HastePlugin class implements the Haste system functionality.
152
153
```javascript { .api }
154
/**
155
* HastePlugin class implementing Haste module resolution
156
*/
157
class HastePlugin implements FileMapPlugin {
158
constructor(options: HastePluginOptions);
159
160
// Plugin interface methods
161
initialize(initOptions: FileMapPluginInitOptions): Promise<void>;
162
assertValid(): void;
163
bulkUpdate(delta: FileMapDelta): Promise<void>;
164
getSerializableSnapshot(): HasteMapData;
165
onRemovedFile(relativeFilePath: string, fileMetadata: FileMetadata): void;
166
onNewOrModifiedFile(relativeFilePath: string, fileMetadata: FileMetadata): void;
167
getCacheKey(): string;
168
169
// HasteMap interface methods
170
getModule(name: string, platform?: string, supportsNativePlatform?: boolean, type?: number): string | null;
171
getPackage(name: string, platform?: string, supportsNativePlatform?: boolean): string | null;
172
computeConflicts(): Array<HasteConflict>;
173
}
174
175
interface HastePluginOptions {
176
console?: Console;
177
enableHastePackages: boolean;
178
perfLogger?: PerfLogger;
179
platforms: Set<string>;
180
rootDir: string;
181
failValidationOnConflicts: boolean;
182
}
183
```
184
185
**Usage Examples:**
186
187
```javascript
188
import { HastePlugin } from "metro-file-map";
189
190
// Create custom Haste plugin
191
const hastePlugin = new HastePlugin({
192
enableHastePackages: true,
193
platforms: new Set(['ios', 'android', 'web']),
194
rootDir: process.cwd(),
195
failValidationOnConflicts: true,
196
console: console
197
});
198
199
// Use in FileMap configuration
200
const fileMap = new FileMap({
201
// ... other options
202
plugins: [hastePlugin],
203
throwOnModuleCollision: true
204
});
205
206
// The plugin will be available in build results
207
const { hasteMap } = await fileMap.build();
208
209
// Validate Haste map (throws on conflicts if configured)
210
try {
211
hastePlugin.assertValid();
212
console.log('Haste map is valid');
213
} catch (error) {
214
console.error('Haste conflicts detected:', error.message);
215
}
216
```
217
218
### Module Name Declaration
219
220
Modules declare their Haste names using comment annotations:
221
222
```javascript { .api }
223
// In your JavaScript/TypeScript files:
224
225
/**
226
* @providesModule Button
227
*/
228
// or
229
/**
230
* @flow
231
* @providesModule Button
232
*/
233
234
// Alternative Flow syntax:
235
// @providesModule Button
236
237
export default class Button extends React.Component {
238
// Component implementation
239
}
240
```
241
242
```javascript { .api }
243
// Package.json based modules (if enableHastePackages: true):
244
{
245
"name": "my-utils",
246
"main": "index.js"
247
}
248
// This package can be resolved as: hasteMap.getPackage('my-utils')
249
```
250
251
## Types
252
253
```javascript { .api }
254
interface HasteMapData {
255
/** Map of module names to platform-specific metadata */
256
modules: Map<string, HasteMapItem>;
257
}
258
259
interface HasteMapItem {
260
/** Platform-specific module metadata */
261
[platform: string]: HasteMapItemMetadata;
262
}
263
264
type HasteMapItemMetadata = [
265
string, // path
266
number // type (MODULE=0, PACKAGE=1)
267
];
268
269
type DuplicatesSet = Map<string, number>;
270
type DuplicatesIndex = Map<string, Map<string, DuplicatesSet>>;
271
272
interface FileMapDelta {
273
removed: Iterable<[string, FileMetadata]>;
274
addedOrModified: Iterable<[string, FileMetadata]>;
275
}
276
```