forked from JavaScriptSolidServer/JavaScriptSolidServer
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathevents.js
More file actions
77 lines (64 loc) · 2.28 KB
/
Copy pathevents.js
File metadata and controls
77 lines (64 loc) · 2.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* Resource Events Emitter
*
* Singleton EventEmitter for resource change notifications.
* Handlers emit 'change' events here, WebSocket broadcasts to subscribers.
*/
import { EventEmitter } from 'events';
import { watch } from 'fs';
import { join, relative } from 'path';
// Singleton event emitter for resource changes
export const resourceEvents = new EventEmitter();
// Increase max listeners since many WebSocket connections may subscribe
resourceEvents.setMaxListeners(1000);
/**
* Emit a resource change event
* @param {string} resourceUrl - Full URL of the changed resource
*/
export function emitChange(resourceUrl) {
resourceEvents.emit('change', resourceUrl);
}
/**
* Start watching filesystem for changes and emit notifications
* @param {string} rootDir - Directory to watch
* @param {string} baseUrl - Base URL for constructing resource URLs (e.g., http://localhost:3000)
*/
export function startFileWatcher(rootDir, baseUrl) {
// Debounce map to avoid duplicate events (editors often save multiple times)
const debounceMap = new Map();
const DEBOUNCE_MS = 100;
try {
const watcher = watch(rootDir, { recursive: true }, (eventType, filename) => {
if (!filename) return;
// Skip hidden files and common temp files
if (filename.startsWith('.') || filename.endsWith('~') || filename.endsWith('.swp')) {
return;
}
// Debounce: skip if we just emitted for this file
const now = Date.now();
const lastEmit = debounceMap.get(filename);
if (lastEmit && now - lastEmit < DEBOUNCE_MS) {
return;
}
debounceMap.set(filename, now);
// Clean up old debounce entries periodically
if (debounceMap.size > 1000) {
for (const [key, time] of debounceMap) {
if (now - time > 5000) debounceMap.delete(key);
}
}
// Construct resource URL
const resourcePath = '/' + filename.replace(/\\/g, '/');
const resourceUrl = baseUrl.replace(/\/$/, '') + resourcePath;
emitChange(resourceUrl);
});
// Handle watcher errors gracefully
watcher.on('error', (err) => {
console.error('File watcher error:', err.message);
});
return watcher;
} catch (err) {
console.error('Failed to start file watcher:', err.message);
return null;
}
}