| 37 | encoding: 'utf8' as const, |
| 38 | } |
| 39 | class RotatingWriteStream { |
| 40 | file: string |
| 41 | writeStream!: fs.WriteStream |
| 42 | size: number |
| 43 | sizeLimit: number |
| 44 | private rotatePromise: Promise<void> | undefined |
| 45 | private drainPromise: Promise<void> | undefined |
| 46 | constructor(file: string, sizeLimit: number) { |
| 47 | this.file = file |
| 48 | this.size = 0 |
| 49 | this.sizeLimit = sizeLimit |
| 50 | this.createWriteStream() |
| 51 | } |
| 52 | private createWriteStream() { |
| 53 | this.writeStream = fs.createWriteStream(this.file, writeStreamOptions) |
| 54 | } |
| 55 | // Recreate the file |
| 56 | private async rotate() { |
| 57 | await this.end() |
| 58 | try { |
| 59 | fs.unlinkSync(this.file) |
| 60 | } catch (err: any) { |
| 61 | // It's fine if the file does not exist yet |
| 62 | if (err.code !== 'ENOENT') { |
| 63 | throw err |
| 64 | } |
| 65 | } |
| 66 | this.size = 0 |
| 67 | this.createWriteStream() |
| 68 | this.rotatePromise = undefined |
| 69 | } |
| 70 | async write(data: string): Promise<void> { |
| 71 | if (this.rotatePromise) await this.rotatePromise |
| 72 | |
| 73 | this.size += data.length |
| 74 | if (this.size > this.sizeLimit) { |
| 75 | await (this.rotatePromise = this.rotate()) |
| 76 | } |
| 77 | |
| 78 | if (!this.writeStream.write(data, 'utf8')) { |
| 79 | if (this.drainPromise === undefined) { |
| 80 | this.drainPromise = new Promise<void>((resolve, _reject) => { |
| 81 | this.writeStream.once('drain', () => { |
| 82 | this.drainPromise = undefined |
| 83 | resolve() |
| 84 | }) |
| 85 | }) |
| 86 | } |
| 87 | await this.drainPromise |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | end(): Promise<void> { |
| 92 | return new Promise((resolve) => { |
| 93 | this.writeStream.end(resolve) |
| 94 | }) |
| 95 | } |
| 96 | } |
nothing calls this directly
no outgoing calls
no test coverage detected