102 lines
2.5 KiB
TypeScript
102 lines
2.5 KiB
TypeScript
import * as fs from "fs";
|
|
import * as path from "path";
|
|
import type { SyncClient, RelativePath } from "sync-client";
|
|
|
|
export class FileWatcher {
|
|
private watcher: fs.FSWatcher | undefined;
|
|
private isRunning = false;
|
|
|
|
public constructor(
|
|
private readonly basePath: string,
|
|
private readonly client: SyncClient
|
|
) {}
|
|
|
|
public start(): void {
|
|
if (this.isRunning) {
|
|
return;
|
|
}
|
|
|
|
this.isRunning = true;
|
|
|
|
this.watcher = fs.watch(
|
|
this.basePath,
|
|
{ recursive: true },
|
|
(eventType, filename) => {
|
|
if (filename === null || filename.length === 0) {
|
|
return;
|
|
}
|
|
|
|
// Convert to forward slashes for consistency
|
|
const relativePath = this.toUnixPath(filename);
|
|
|
|
if (eventType === "rename") {
|
|
this.handleRenameOrDelete(relativePath);
|
|
} else {
|
|
// Must be "change" event
|
|
this.handleChange(relativePath);
|
|
}
|
|
}
|
|
);
|
|
|
|
this.client.logger.info("File watcher started");
|
|
}
|
|
|
|
public stop(): void {
|
|
if (this.watcher !== undefined) {
|
|
this.watcher.close();
|
|
this.watcher = undefined;
|
|
}
|
|
this.isRunning = false;
|
|
this.client.logger.info("File watcher stopped");
|
|
}
|
|
|
|
private handleChange(relativePath: RelativePath): void {
|
|
this.client
|
|
.syncLocallyUpdatedFile({ relativePath })
|
|
.catch((err: unknown) => {
|
|
this.client.logger.error(
|
|
`Failed to sync updated file ${relativePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
);
|
|
});
|
|
}
|
|
|
|
private handleRenameOrDelete(relativePath: RelativePath): void {
|
|
const fullPath = path.join(this.basePath, relativePath);
|
|
|
|
fs.access(fullPath, fs.constants.F_OK, (accessError) => {
|
|
if (accessError) {
|
|
this.client
|
|
.syncLocallyDeletedFile(relativePath)
|
|
.catch((deleteErr: unknown) => {
|
|
this.client.logger.error(
|
|
`Failed to sync deleted file ${relativePath}: ${deleteErr instanceof Error ? deleteErr.message : String(deleteErr)}`
|
|
);
|
|
});
|
|
} else {
|
|
fs.stat(fullPath, (statErr, stats) => {
|
|
if (statErr !== null || !stats.isFile()) {
|
|
return;
|
|
}
|
|
|
|
this.client
|
|
.syncLocallyCreatedFile(relativePath)
|
|
.catch((createErr: unknown) => {
|
|
this.client.logger.error(
|
|
`Failed to sync created file ${relativePath}: ${createErr instanceof Error ? createErr.message : String(createErr)}`
|
|
);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Convert a native platform path to forward slashes
|
|
*/
|
|
private toUnixPath(nativePath: string): string {
|
|
if (path.sep === "\\") {
|
|
return nativePath.replace(/\\/g, "/");
|
|
}
|
|
return nativePath;
|
|
}
|
|
}
|