or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cache-management.mderror-handling.mdfile-map-core.mdfile-system.mdhaste-system.mdindex.mdplugin-system.md

haste-system.mddocs/

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

```