-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: introduce `watch` mode * ci: add `watch` tests * ci: fix Deno permissions * ci: fix tests * ci: fix tests * docs: add `--watch` and `--watch-interval`
- Loading branch information
1 parent
7b94a13
commit 8b816f7
Showing
10 changed files
with
476 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ node_modules | |
/coverage | ||
.env | ||
.nyc_output | ||
/.temp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* c8 ignore start */ | ||
|
||
import { | ||
stat as nodeStat, | ||
readdir as nodeReaddir, | ||
type Dirent, | ||
type Stats, | ||
} from 'node:fs'; | ||
|
||
export const readdir = ( | ||
path: string, | ||
options: { withFileTypes: true } | ||
): Promise<Dirent[]> => | ||
new Promise((resolve, reject) => { | ||
nodeReaddir(path, options, (err, entries) => { | ||
if (err) reject(err); | ||
else resolve(entries); | ||
}); | ||
}); | ||
|
||
export const stat = (path: string): Promise<Stats> => { | ||
return new Promise((resolve, reject) => { | ||
nodeStat(path, (err, stats) => { | ||
if (err) reject(err); | ||
else resolve(stats); | ||
}); | ||
}); | ||
}; | ||
|
||
/* c8 ignore stop */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* c8 ignore next */ | ||
import { watch as nodeWatch, type FSWatcher } from 'node:fs'; | ||
import { join } from 'node:path'; | ||
import { readdir, stat } from '../polyfills/fs.js'; | ||
import { listFiles } from '../modules/list-files.js'; | ||
|
||
/* c8 ignore next */ | ||
export type WatchCallback = (file: string, event: string) => void; | ||
|
||
class Watcher { | ||
private rootDir: string; | ||
private files: string[] = []; | ||
private fileWatchers: Map<string, FSWatcher> = new Map(); | ||
private dirWatchers: Map<string, FSWatcher> = new Map(); | ||
private callback: WatchCallback; | ||
|
||
constructor(rootDir: string, callback: WatchCallback) { | ||
this.rootDir = rootDir; | ||
this.callback = callback; | ||
} | ||
|
||
private watchFile(filePath: string) { | ||
/* c8 ignore next */ | ||
if (this.fileWatchers.has(filePath)) return; | ||
|
||
const watcher = nodeWatch(filePath, (eventType) => { | ||
this.callback(filePath, eventType); | ||
}); | ||
|
||
/* c8 ignore start */ | ||
watcher.on('error', () => { | ||
return; | ||
}); | ||
/* c8 ignore stop */ | ||
|
||
this.fileWatchers.set(filePath, watcher); | ||
} | ||
|
||
private unwatchFiles() { | ||
for (const [filePath, watcher] of this.fileWatchers) { | ||
watcher.close(); | ||
this.fileWatchers.delete(filePath); | ||
} | ||
} | ||
|
||
private watchFiles(filePaths: string[]) { | ||
this.unwatchFiles(); | ||
|
||
for (const filePath of filePaths) { | ||
this.watchFile(filePath); | ||
} | ||
} | ||
|
||
private async watchDirectory(dir: string) { | ||
if (this.dirWatchers.has(dir)) return; | ||
|
||
const watcher = nodeWatch(dir, async (_, filename) => { | ||
if (filename) { | ||
const fullPath = join(dir, filename); | ||
|
||
this.files = await listFiles(this.rootDir); | ||
this.watchFiles(this.files); | ||
|
||
try { | ||
const stats = await stat(fullPath); | ||
if (stats.isDirectory()) await this.watchDirectory(fullPath); | ||
/* c8 ignore start */ | ||
} catch {} | ||
/* c8 ignore stop */ | ||
} | ||
}); | ||
|
||
/* c8 ignore start */ | ||
watcher.on('error', () => { | ||
return; | ||
}); | ||
/* c8 ignore stop */ | ||
|
||
this.dirWatchers.set(dir, watcher); | ||
|
||
const entries = await readdir(dir, { withFileTypes: true }); | ||
|
||
for (const entry of entries) { | ||
if (entry.isDirectory()) { | ||
const fullPath = join(dir, entry.name); | ||
await this.watchDirectory(fullPath); | ||
} | ||
} | ||
} | ||
|
||
public async start() { | ||
try { | ||
const stats = await stat(this.rootDir); | ||
|
||
if (stats.isDirectory()) { | ||
this.files = await listFiles(this.rootDir); | ||
|
||
this.watchFiles(this.files); | ||
await this.watchDirectory(this.rootDir); | ||
} else this.watchFile(this.rootDir); | ||
/* c8 ignore start */ | ||
} catch (err) { | ||
throw new Error( | ||
`Path does not exist or is not accessible: ${this.rootDir}` | ||
); | ||
} | ||
/* c8 ignore stop */ | ||
} | ||
|
||
public stop() { | ||
this.unwatchFiles(); | ||
this.unwatchDirectories(); | ||
} | ||
|
||
private unwatchDirectories() { | ||
for (const [dirPath, watcher] of this.dirWatchers) { | ||
watcher.close(); | ||
this.dirWatchers.delete(dirPath); | ||
} | ||
} | ||
/* c8 ignore next */ | ||
} | ||
|
||
/* c8 ignore next */ | ||
export const watch = async (path: string, callback: WatchCallback) => { | ||
const watcher = new Watcher(path, callback); | ||
|
||
await watcher.start(); | ||
|
||
return watcher; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.