1 | import fs from 'fs';
|
2 |
|
3 | import { Task } from '@stryker-mutator/util';
|
4 | import { mergeMap, Subject } from 'rxjs';
|
5 | import { Disposable } from 'typed-inject';
|
6 |
|
7 | const MAX_CONCURRENT_FILE_IO = 256;
|
8 |
|
9 | class FileSystemAction<TOut> {
|
10 | public readonly task = new Task<TOut>();
|
11 |
|
12 | |
13 |
|
14 |
|
15 | constructor(private readonly work: () => Promise<TOut>) {}
|
16 |
|
17 | public async execute() {
|
18 | try {
|
19 | const output = await this.work();
|
20 | this.task.resolve(output);
|
21 | } catch (err) {
|
22 | this.task.reject(err);
|
23 | }
|
24 | }
|
25 | }
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | export class FileSystem implements Disposable {
|
33 | private readonly todoSubject = new Subject<FileSystemAction<any>>();
|
34 | private readonly subscription = this.todoSubject
|
35 | .pipe(
|
36 | mergeMap(async (action) => {
|
37 | await action.execute();
|
38 | }, MAX_CONCURRENT_FILE_IO)
|
39 | )
|
40 | .subscribe();
|
41 |
|
42 | public dispose(): void {
|
43 | this.subscription.unsubscribe();
|
44 | }
|
45 |
|
46 | public readonly readFile = this.forward('readFile');
|
47 | public readonly copyFile = this.forward('copyFile');
|
48 | public readonly writeFile = this.forward('writeFile');
|
49 | public readonly mkdir = this.forward('mkdir');
|
50 | public readonly readdir = this.forward('readdir');
|
51 |
|
52 | private forward<TMethod extends keyof Omit<typeof fs.promises, 'constants'>>(method: TMethod): (typeof fs.promises)[TMethod] {
|
53 | return (...args: any[]) => {
|
54 | const action = new FileSystemAction(() => (fs.promises[method] as any)(...args));
|
55 | this.todoSubject.next(action);
|
56 | return action.task.promise as any;
|
57 | };
|
58 | }
|
59 | }
|