-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(core): refactor watch use FileSystemWatcher
- Loading branch information
1 parent
e68bed1
commit c6d7dd1
Showing
9 changed files
with
283 additions
and
78 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
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
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,104 @@ | ||
import { virtualFs } from '@angular-devkit/core'; | ||
import temp from 'temp'; | ||
import fs from 'fs'; | ||
import { DocgeniNodeJsAsyncHost } from './node-host'; | ||
import { normalize, resolve } from './path'; | ||
import { Observable } from 'rxjs'; | ||
import { FileSystemWatcher, HostWatchEventType } from './watcher'; | ||
import { toolkit } from '@docgeni/toolkit'; | ||
import chokidar from 'chokidar'; | ||
|
||
describe('#fs-watcher', () => { | ||
let root: string; | ||
let host: virtualFs.Host<fs.Stats>; | ||
|
||
beforeEach(() => { | ||
root = temp.mkdirSync('core-node-spec-'); | ||
host = new DocgeniNodeJsAsyncHost(); | ||
}); | ||
|
||
afterEach(async () => { | ||
await host.delete(normalize(root)).toPromise(); | ||
}); | ||
|
||
it('watch files', async () => { | ||
const fsWatcher = new FileSystemWatcher(); | ||
const content = virtualFs.stringToFileBuffer('new content'); | ||
const file1Path = root + '/sub1/file1.txt'; | ||
const file2Path = root + '/sub1/file2.txt'; | ||
const file3Path = root + '/sub1/file3.txt'; | ||
fs.mkdirSync(root + '/sub1'); | ||
fs.writeFileSync(file1Path, 'hello world'); | ||
fs.writeFileSync(file2Path, 'hello world'); | ||
|
||
const allEvents: virtualFs.HostWatchEvent[] = []; | ||
fsWatcher.watch([file1Path, file2Path]).subscribe(change => { | ||
allEvents.push(change); | ||
}); | ||
await toolkit.utils.wait(10); | ||
await host.write(normalize(file1Path), content).toPromise(); | ||
await toolkit.utils.wait(10); | ||
await host.write(normalize(file3Path), content).toPromise(); | ||
await toolkit.utils.wait(10); | ||
await host.delete(normalize(file2Path)).toPromise(); | ||
await toolkit.utils.wait(2000); | ||
expect(allEvents.length).toEqual(2); | ||
await fsWatcher.close(); | ||
}); | ||
|
||
it('watch dir', async () => { | ||
const fsWatcher = new FileSystemWatcher(); | ||
const content = virtualFs.stringToFileBuffer('hello world'); | ||
const file1Path = root + '/sub1/file1.txt'; | ||
const file2Path = root + '/sub1/file2.txt'; | ||
const file3Path = root + '/sub1/sub2/file3.txt'; | ||
fs.mkdirSync(root + '/sub1'); | ||
fs.writeFileSync(file1Path, 'hello world'); | ||
|
||
const allEvents: virtualFs.HostWatchEvent[] = []; | ||
fsWatcher.watch(root + '/sub1').subscribe(change => { | ||
allEvents.push(change); | ||
}); | ||
await toolkit.utils.wait(10); | ||
await host.write(normalize(file2Path), content).toPromise(); | ||
await toolkit.utils.wait(10); | ||
await host.write(normalize(file3Path), content).toPromise(); | ||
await toolkit.utils.wait(10); | ||
await host.delete(normalize(file1Path)).toPromise(); | ||
await toolkit.utils.wait(2000); | ||
expect(allEvents.length).toEqual(3); | ||
await fsWatcher.close(); | ||
}); | ||
|
||
it('watch aggregated', async () => { | ||
const fsWatcher = new FileSystemWatcher(); | ||
const content = virtualFs.stringToFileBuffer('new content'); | ||
const file1Path = root + '/sub1/file1.txt'; | ||
const file2Path = root + '/sub1/file2.txt'; | ||
const file3Path = root + '/sub1/file3.txt'; | ||
fs.mkdirSync(root + '/sub1'); | ||
fs.writeFileSync(file1Path, 'hello world'); | ||
fs.writeFileSync(file2Path, 'hello world'); | ||
|
||
let allEvents: virtualFs.HostWatchEvent[] = []; | ||
fsWatcher.watch(root + '/sub1'); | ||
fsWatcher.aggregated(3000).subscribe(value => { | ||
allEvents = value; | ||
}); | ||
await toolkit.utils.wait(10); | ||
await host.write(normalize(file1Path), content).toPromise(); | ||
await toolkit.utils.wait(10); | ||
await host.write(normalize(file3Path), content).toPromise(); | ||
await toolkit.utils.wait(10); | ||
await host.delete(normalize(file2Path)).toPromise(); | ||
await toolkit.utils.wait(4000); | ||
expect(allEvents.length).toEqual(3); | ||
expect(allEvents[0].path).toEqual(file1Path); | ||
expect(allEvents[0].type).toEqual(HostWatchEventType.Changed); | ||
expect(allEvents[1].path).toEqual(file3Path); | ||
expect(allEvents[1].type).toEqual(HostWatchEventType.Created); | ||
expect(allEvents[2].path).toEqual(file2Path); | ||
expect(allEvents[2].type).toEqual(HostWatchEventType.Deleted); | ||
await fsWatcher.close(); | ||
}); | ||
}); |
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,101 @@ | ||
import { Observable, Subject } from 'rxjs'; | ||
import { FSWatcher, WatchOptions } from 'chokidar'; | ||
import { getSystemPath, virtualFs } from '@angular-devkit/core'; | ||
import { normalize } from './path'; | ||
import { toolkit } from '@docgeni/toolkit'; | ||
|
||
export enum HostWatchEventType { | ||
Changed = 0, | ||
Created = 1, | ||
Deleted = 2, | ||
Renamed = 3 | ||
} | ||
|
||
export class FileSystemWatcher { | ||
private watcher: FSWatcher; | ||
private events$ = new Subject<virtualFs.HostWatchEvent>(); | ||
private aggregatedEvents$ = new Subject<virtualFs.HostWatchEvent[]>(); | ||
private aggregateTimeout: NodeJS.Timeout; | ||
private aggregatedEvents: virtualFs.HostWatchEvent[] = []; | ||
|
||
constructor(options: WatchOptions = { persistent: true, ignoreInitial: true }) { | ||
this.watcher = new FSWatcher(options); | ||
this.initialize(); | ||
} | ||
|
||
private emitEvent(path: string, type: HostWatchEventType) { | ||
this.events$.next({ | ||
path: normalize(path), | ||
time: new Date(), | ||
type: (type as unknown) as virtualFs.HostWatchEventType | ||
}); | ||
} | ||
|
||
private initialize() { | ||
this.watcher | ||
.on('change', path => { | ||
this.emitEvent(path, HostWatchEventType.Changed); | ||
}) | ||
.on('add', path => { | ||
this.emitEvent(path, HostWatchEventType.Created); | ||
}) | ||
.on('unlink', path => { | ||
this.emitEvent(path, HostWatchEventType.Deleted); | ||
}); | ||
} | ||
|
||
watch(paths: string | string[]): Observable<virtualFs.HostWatchEvent> { | ||
// this.watcher.add(paths); | ||
this.watcher.add(toolkit.utils.coerceArray(paths).map(path => getSystemPath(normalize(path)))); | ||
return this.events$.asObservable(); | ||
} | ||
|
||
aggregated(aggregateInterval: number = 2000): Observable<virtualFs.HostWatchEvent[]> { | ||
// return new Observable(subscribe => { | ||
// const aggregatedEvents: virtualFs.HostWatchEvent[] = []; | ||
// let aggregateTimeout: NodeJS.Timeout; | ||
// const subscription = this.events$.subscribe(event => { | ||
// if (aggregateTimeout) { | ||
// // eslint-disable-next-line no-restricted-globals | ||
// clearTimeout(aggregateTimeout); | ||
// } | ||
// aggregatedEvents.push(event); | ||
|
||
// // eslint-disable-next-line no-restricted-globals | ||
// aggregateTimeout = setTimeout(() => { | ||
// subscribe.next(aggregatedEvents); | ||
// }, aggregateInterval); | ||
// }); | ||
// return () => subscription.unsubscribe(); | ||
// }); | ||
this.events$.subscribe(event => { | ||
if (this.aggregateTimeout) { | ||
// eslint-disable-next-line no-restricted-globals | ||
clearTimeout(this.aggregateTimeout); | ||
} | ||
this.aggregatedEvents.push(event); | ||
|
||
// eslint-disable-next-line no-restricted-globals | ||
this.aggregateTimeout = setTimeout(() => { | ||
this.aggregatedEvents$.next(this.aggregatedEvents); | ||
this.aggregatedEvents = []; | ||
this.aggregateTimeout = null; | ||
}, 2000); | ||
}); | ||
return this.aggregatedEvents$; | ||
} | ||
|
||
async close() { | ||
if (this.aggregateTimeout) { | ||
// eslint-disable-next-line no-restricted-globals | ||
clearTimeout(this.aggregateTimeout); | ||
this.aggregatedEvents$.next(this.aggregatedEvents); | ||
this.aggregatedEvents = []; | ||
this.aggregateTimeout = null; | ||
} | ||
this.events$.complete(); | ||
this.aggregatedEvents$.complete(); | ||
this.watcher.removeAllListeners(); | ||
await this.watcher.close(); | ||
} | ||
} |
Oops, something went wrong.