or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

file-system-mapping.mdfile-system-watching.mdindex.mdmodule-resolution.mdvirtual-file-system.md
tile.json

file-system-watching.mddocs/

0

# File System Watching

1

2

Real-time file system monitoring for interactive development tools, providing change events and automatic map updates. The watching system supports multiple backends including Watchman, FSEvents, and Node.js fs.watch, with intelligent change detection and batching.

3

4

## Capabilities

5

6

### Watch Mode Configuration

7

8

Configure file system watching when creating the haste map.

9

10

```typescript { .api }

11

/**

12

* Enable file system watching in HasteMap options

13

*/

14

interface WatchOptions extends Options {

15

/** Enable file system watching, defaults to false */

16

watch?: boolean;

17

/** Use Watchman for file system monitoring, defaults to true */

18

useWatchman?: boolean;

19

/** Whether to throw errors on module name collisions in watch mode, defaults to false */

20

throwOnModuleCollision?: boolean;

21

}

22

```

23

24

### Change Event Handling

25

26

Subscribe to file system changes through the EventEmitter interface.

27

28

```typescript { .api }

29

/**

30

* HasteMap extends EventEmitter to provide change notifications

31

*/

32

interface IHasteMap extends EventEmitter {

33

/**

34

* Subscribe to file system change events

35

* @param eventType - Event type, currently only 'change' is supported

36

* @param handler - Function to handle change events

37

*/

38

on(eventType: 'change', handler: (event: ChangeEvent) => void): void;

39

}

40

41

/**

42

* Change event data structure providing access to changed files and updated maps

43

*/

44

interface ChangeEvent {

45

/** Queue of file system events that triggered this change */

46

eventsQueue: EventsQueue;

47

/** Updated HasteFS instance with current file system state */

48

hasteFS: IHasteFS;

49

/** Updated ModuleMap instance with current module mappings */

50

moduleMap: IModuleMap;

51

}

52

53

/**

54

* Queue of individual file system events

55

*/

56

interface EventsQueue extends Array<{

57

/** Absolute path to the changed file */

58

filePath: string;

59

/** File statistics, undefined for deleted files */

60

stat: Stats | undefined;

61

/** Type of change: 'add', 'change', or 'unlink' */

62

type: string;

63

}> {}

64

```

65

66

### Lifecycle Management

67

68

Methods for managing the watching lifecycle.

69

70

```typescript { .api }

71

/**

72

* Stop file system watching and cleanup resources

73

* @returns Promise that resolves when all watchers are closed

74

*/

75

end(): Promise<void>;

76

```

77

78

**Usage Examples:**

79

80

```typescript

81

import HasteMap from "jest-haste-map";

82

83

// Create haste map with watching enabled

84

const hasteMap = await HasteMap.create({

85

id: "my-project",

86

extensions: ["js", "ts", "jsx", "tsx"],

87

maxWorkers: 4,

88

platforms: ["ios", "android"],

89

roots: ["/src"],

90

retainAllFiles: true,

91

rootDir: "/project/root",

92

watch: true, // Enable watching

93

useWatchman: true, // Prefer Watchman if available

94

});

95

96

// Build initial map

97

const {hasteFS, moduleMap} = await hasteMap.build();

98

99

// Set up change handler

100

hasteMap.on('change', (event) => {

101

console.log(`Detected ${event.eventsQueue.length} file system changes:`);

102

103

for (const fileEvent of event.eventsQueue) {

104

console.log(` ${fileEvent.type}: ${fileEvent.filePath}`);

105

106

if (fileEvent.stat) {

107

console.log(` Size: ${fileEvent.stat.size} bytes`);

108

console.log(` Modified: ${fileEvent.stat.mtime}`);

109

}

110

}

111

112

// Access updated file system

113

const updatedFiles = event.hasteFS.getAllFiles();

114

console.log(`Total files now: ${updatedFiles.length}`);

115

116

// Check specific module resolution

117

const appPath = event.moduleMap.getModule('App');

118

if (appPath) {

119

console.log(`App module resolved to: ${appPath}`);

120

121

// Get updated dependencies

122

const appDeps = event.hasteFS.getDependencies(appPath);

123

console.log(`App dependencies: ${appDeps?.length || 0}`);

124

}

125

});

126

127

// In a real application, keep the process running

128

// The change handler will be called whenever files change

129

console.log("Watching for file changes...");

130

131

// Clean up when shutting down

132

process.on('SIGINT', async () => {

133

console.log("Shutting down file watcher...");

134

await hasteMap.end();

135

process.exit(0);

136

});

137

```

138

139

### Watcher Backend Selection

140

141

The watching system automatically selects the best available backend:

142

143

1. **Watchman** (preferred): Facebook's file watching service, optimal for large projects

144

2. **FSEvents** (macOS): Native macOS file system events, efficient for Mac development

145

3. **NodeWatcher**: Cross-platform fallback using Node.js fs.watch

146

147

```typescript

148

// The backend selection is automatic, but you can control preferences

149

const hasteMap = await HasteMap.create({

150

id: "my-project",

151

extensions: ["js", "ts"],

152

maxWorkers: 4,

153

platforms: [],

154

roots: ["/src"],

155

retainAllFiles: true,

156

rootDir: "/project/root",

157

watch: true,

158

useWatchman: false, // Disable Watchman, will use FSEvents or NodeWatcher

159

});

160

```

161

162

### Change Detection and Batching

163

164

The watching system includes intelligent change detection and batching:

165

166

- **Duplicate Detection**: Filters out duplicate events for the same file

167

- **Change Batching**: Groups rapid changes into single events (30ms intervals)

168

- **Mtime Checking**: Only processes files that have actually changed

169

- **Incremental Updates**: Only re-processes files that have changed, not the entire project

170

171

```typescript

172

// Example of handling batched changes

173

hasteMap.on('change', (event) => {

174

const changedFiles = event.eventsQueue.filter(e => e.type === 'change');

175

const addedFiles = event.eventsQueue.filter(e => e.type === 'add');

176

const deletedFiles = event.eventsQueue.filter(e => e.type === 'unlink');

177

178

console.log(`Batch contains: ${changedFiles.length} changed, ${addedFiles.length} added, ${deletedFiles.length} deleted`);

179

180

// Process different types of changes

181

for (const added of addedFiles) {

182

console.log(`New file detected: ${added.filePath}`);

183

184

// Check if it's a new module

185

const moduleName = event.hasteFS.getModuleName(added.filePath);

186

if (moduleName) {

187

console.log(` New module available: ${moduleName}`);

188

}

189

}

190

191

for (const deleted of deletedFiles) {

192

console.log(`File deleted: ${deleted.filePath}`);

193

194

// Note: deleted files won't exist in the updated hasteFS

195

const stillExists = event.hasteFS.exists(deleted.filePath);

196

console.log(` Still in map: ${stillExists}`); // Should be false

197

}

198

});

199

```

200

201

### Integration with Development Tools

202

203

File system watching is particularly useful for development tools:

204

205

```typescript

206

// Development server integration

207

class DevServer {

208

private hasteMap: IHasteMap;

209

private clients: WebSocket[] = [];

210

211

async start() {

212

this.hasteMap = await HasteMap.create({

213

id: "dev-server",

214

extensions: ["js", "ts", "jsx", "tsx", "css", "json"],

215

maxWorkers: 4,

216

platforms: [],

217

roots: ["/src", "/public"],

218

retainAllFiles: true,

219

rootDir: process.cwd(),

220

watch: true,

221

});

222

223

await this.hasteMap.build();

224

225

// Watch for changes and notify clients

226

this.hasteMap.on('change', (event) => {

227

const changes = event.eventsQueue.map(e => ({

228

type: e.type,

229

path: e.filePath,

230

size: e.stat?.size,

231

}));

232

233

// Notify all connected clients

234

this.clients.forEach(client => {

235

client.send(JSON.stringify({

236

type: 'file-change',

237

changes,

238

}));

239

});

240

241

// Rebuild if package.json changed

242

const packageJsonChanged = event.eventsQueue.some(

243

e => e.filePath.endsWith('package.json')

244

);

245

246

if (packageJsonChanged) {

247

console.log('package.json changed, triggering rebuild...');

248

this.rebuild();

249

}

250

});

251

}

252

253

async stop() {

254

await this.hasteMap.end();

255

}

256

257

private rebuild() {

258

// Restart the server or rebuild assets

259

}

260

}

261

```

262

263

### Error Handling in Watch Mode

264

265

Watch mode automatically handles many error conditions:

266

267

- **File Access Errors**: Files that become inaccessible are automatically removed from the map

268

- **Permission Errors**: Gracefully handled, with warnings logged

269

- **Watcher Failures**: Automatic fallback to different watcher backends

270

- **Module Collisions**: In watch mode, collisions generate warnings instead of errors by default

271

272

```typescript

273

// Watch mode error handling

274

const hasteMap = await HasteMap.create({

275

id: "my-project",

276

extensions: ["js", "ts"],

277

maxWorkers: 4,

278

platforms: [],

279

roots: ["/src"],

280

retainAllFiles: true,

281

rootDir: "/project/root",

282

watch: true,

283

throwOnModuleCollision: false, // Warn instead of throw in watch mode

284

console: {

285

// Custom console for handling warnings

286

log: console.log,

287

warn: (message) => {

288

console.warn(`[Haste Warning] ${message}`);

289

},

290

error: console.error,

291

},

292

});

293

294

hasteMap.on('change', (event) => {

295

// Changes are automatically applied even if some files had errors

296

console.log('Map updated successfully despite any individual file errors');

297

});

298

```