UNPKG

6.48 kBPlain TextView Raw
1import from2 from "from2";
2import fs from "fs";
3import libReport from "istanbul-lib-report";
4import merge2 from "merge2";
5import sysPath from "path";
6import stream from "stream";
7import Vinyl from "vinyl";
8import { ReportOptions, StreamReporter, VinylReporter } from "./reporter";
9
10export interface ContextOptions extends libReport.Context {
11 writer: libReport.FileWriter;
12}
13
14function createContext(options: ContextOptions): any {
15 const context: libReport.Context = libReport.createContext(options);
16 if (context.writer !== options.writer) {
17 Object.defineProperty(context, "writer", {value: options.writer});
18 }
19 return context;
20}
21
22export function wrapFileReporter(reporter: libReport.Visitor): VinylReporter {
23 function reportVinyl(options: Readonly<ReportOptions>): NodeJS.ReadableStream {
24 let done: boolean = false;
25 return from2({objectMode: true}, (_: number, next: (err: null | Error, obj: Vinyl) => void): void => {
26 if (done) {
27 next(null, null as any); // end of stream
28 return;
29 }
30 const cwd: string = process.cwd();
31 const writer: VinylWriter = new VinylWriter(cwd, cwd, next);
32 // TODO: Fix istanbul-lib-report types
33 const context: libReport.Context = createContext({
34 writer,
35 sourceFinder: options.sourceFinder,
36 } as any as ContextOptions);
37 const tree: libReport.Tree = libReport.summarizers.pkg(options.map);
38 tree.visit(reporter, context);
39 done = true;
40 });
41 }
42
43 return {reportVinyl};
44}
45
46class VinylWriter implements libReport.FileWriter {
47 public readonly base: string;
48 public readonly dir: string;
49 private readonly next: (err: null | Error, obj: Vinyl) => void;
50
51 constructor(base: string, dir: string, next: (err: null | Error, obj: Vinyl) => void) {
52 this.base = base;
53 this.dir = dir;
54 this.next = next;
55 }
56
57 public copyFile(source: string, dest: string): void {
58 const content: Buffer = fs.readFileSync(source);
59 const resolvedPath: string = sysPath.join(this.dir, dest);
60 const vinyl: Vinyl = new Vinyl({
61 cwd: this.base,
62 base: this.base,
63 path: resolvedPath,
64 contents: content,
65 });
66 this.next(null, vinyl);
67 }
68
69 public writeFile(file: string | null): VinylContentWriter {
70 if (typeof file !== "string") {
71 throw new Error("NotSupported: StreamWriter#writeFile(file: null)");
72 }
73 const resolvedPath: string = sysPath.join(this.dir, file);
74 return new VinylContentWriter((content: Buffer): void => {
75 const vinyl: Vinyl = new Vinyl({
76 cwd: this.base,
77 base: this.base,
78 path: resolvedPath,
79 contents: content,
80 });
81 this.next(null, vinyl);
82 });
83 }
84
85 public writeForDir(subdir: string): VinylWriter {
86 return new VinylWriter(this.base, sysPath.join(this.dir, subdir), this.next);
87 }
88}
89
90class VinylContentWriter implements libReport.ContentWriter {
91 private readonly chunks: string[];
92 private readonly done: (content: Buffer) => void;
93
94 constructor(done: (content: Buffer) => void) {
95 this.chunks = [];
96 this.done = done;
97 }
98
99 public write(str: string): void {
100 this.chunks.push(str);
101 }
102
103 public println(str: string) {
104 this.write(str);
105 this.write("\n");
106 }
107
108 public colorize(str: string): string {
109 return str;
110 }
111
112 public close(): void {
113 return this.done(Buffer.from(this.chunks.join("")));
114 }
115}
116
117export function wrapStreamReporter(reporter: libReport.Visitor): StreamReporter {
118 function reportStream(options: Readonly<ReportOptions>): NodeJS.ReadableStream {
119 const duplex: stream.Duplex = new stream.PassThrough();
120 const writer: StreamWriter = new StreamWriter(duplex);
121 const context: libReport.Context = createContext({
122 writer,
123 sourceFinder: options.sourceFinder,
124 } as any as ContextOptions);
125 const tree: libReport.Tree = libReport.summarizers.pkg(options.map);
126 tree.visit(reporter, context);
127 return duplex;
128 }
129
130 return {reportStream};
131}
132
133class StreamWriter implements libReport.FileWriter {
134 private readonly stream: NodeJS.WritableStream;
135 private fileName: string | null | undefined;
136
137 constructor(stream: NodeJS.WritableStream) {
138 this.stream = stream;
139 this.fileName = undefined;
140 }
141
142 public copyFile(source: string, dest: string): void {
143 throw new Error("NotSupported: StreamWriter#copyFile");
144 }
145
146 public writeFile(file: string | null): StreamContentWriter {
147 if (typeof file !== "string") {
148 file = null;
149 }
150 if (this.fileName === undefined) {
151 this.fileName = file;
152 } else if (file !== this.fileName) {
153 throw new Error(`NotSupported: Write to multiple different files: ${this.fileName}, ${file}`);
154 }
155
156 return new StreamContentWriter(this.stream);
157 }
158
159 public writeForDir(subdir: string): StreamWriter {
160 throw new Error("NotSupported: StreamWriter#writeForDir");
161 }
162}
163
164class StreamContentWriter implements libReport.ContentWriter {
165 private readonly stream: NodeJS.WritableStream;
166
167 constructor(stream: NodeJS.WritableStream) {
168 this.stream = stream;
169 }
170
171 public write(str: string): void {
172 this.stream.write(Buffer.from(str));
173 }
174
175 public println(str: string) {
176 this.write(str);
177 this.write("\n");
178 }
179
180 public colorize(str: string): string {
181 return str;
182 }
183
184 public close(): void {
185 this.stream.end();
186 }
187}
188
189export function toVinylOnlyReporter(reporter: StreamReporter, fileName: string): VinylReporter {
190 function reportVinyl(options: Readonly<ReportOptions>): NodeJS.ReadableStream {
191 let done: boolean = false;
192 const wrappedStream: NodeJS.ReadableStream = from2(
193 {objectMode: true},
194 (_: number, next: (err: null | Error, obj: Vinyl) => void): void => {
195 if (done) {
196 next(null, null as any); // end of stream
197 return;
198 }
199 const stream: NodeJS.ReadableStream = reporter.reportStream(options);
200 next(null, streamToVinyl(stream, fileName));
201 done = true;
202 },
203 );
204 const streams: NodeJS.ReadableStream[] = [wrappedStream];
205 if (reporter.reportVinyl !== undefined) {
206 streams.push(reporter.reportVinyl(options));
207 }
208 return merge2(streams);
209 }
210
211 return {reportVinyl};
212}
213
214function streamToVinyl(stream: NodeJS.ReadableStream, fileName: string): Vinyl {
215 const cwd: string = process.cwd();
216 const resolvedPath: string = sysPath.join(cwd, fileName);
217 return new Vinyl({
218 cwd,
219 base: cwd,
220 path: resolvedPath,
221 contents: stream,
222 });
223}