UNPKG

1.74 kBPlain TextView Raw
1import fs from 'fs';
2
3import { Task } from '@stryker-mutator/util';
4import { mergeMap, Subject } from 'rxjs';
5import { Disposable } from 'typed-inject';
6
7const MAX_CONCURRENT_FILE_IO = 256;
8
9class FileSystemAction<TOut> {
10 public readonly task = new Task<TOut>();
11
12 /**
13 * @param work The task, where a resource and input is presented
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 * A wrapper around nodejs's 'fs' core module, for dependency injection purposes.
29 *
30 * Also has but with build-in buffering with a concurrency limit (like graceful-fs).
31 */
32export 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}