1 | ;
|
2 | /*
|
3 | Copyright 2012-2015, Yahoo Inc.
|
4 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
|
5 | */
|
6 | const path = require('path');
|
7 | const fs = require('fs');
|
8 | const mkdirp = require('make-dir');
|
9 | const supportsColor = require('supports-color');
|
10 |
|
11 | /**
|
12 | * Base class for writing content
|
13 | * @class ContentWriter
|
14 | * @constructor
|
15 | */
|
16 | class ContentWriter {
|
17 | /**
|
18 | * returns the colorized version of a string. Typically,
|
19 | * content writers that write to files will return the
|
20 | * same string and ones writing to a tty will wrap it in
|
21 | * appropriate escape sequences.
|
22 | * @param {String} str the string to colorize
|
23 | * @param {String} clazz one of `high`, `medium` or `low`
|
24 | * @returns {String} the colorized form of the string
|
25 | */
|
26 | colorize(str /*, clazz*/) {
|
27 | return str;
|
28 | }
|
29 |
|
30 | /**
|
31 | * writes a string appended with a newline to the destination
|
32 | * @param {String} str the string to write
|
33 | */
|
34 | println(str) {
|
35 | this.write(`${str}\n`);
|
36 | }
|
37 |
|
38 | /**
|
39 | * closes this content writer. Should be called after all writes are complete.
|
40 | */
|
41 | close() {}
|
42 | }
|
43 |
|
44 | /**
|
45 | * a content writer that writes to a file
|
46 | * @param {Number} fd - the file descriptor
|
47 | * @extends ContentWriter
|
48 | * @constructor
|
49 | */
|
50 | class FileContentWriter extends ContentWriter {
|
51 | constructor(fd) {
|
52 | super();
|
53 |
|
54 | this.fd = fd;
|
55 | }
|
56 |
|
57 | write(str) {
|
58 | fs.writeSync(this.fd, str);
|
59 | }
|
60 |
|
61 | close() {
|
62 | fs.closeSync(this.fd);
|
63 | }
|
64 | }
|
65 |
|
66 | // allow stdout to be captured for tests.
|
67 | let capture = false;
|
68 | let output = '';
|
69 |
|
70 | /**
|
71 | * a content writer that writes to the console
|
72 | * @extends ContentWriter
|
73 | * @constructor
|
74 | */
|
75 | class ConsoleWriter extends ContentWriter {
|
76 | write(str) {
|
77 | if (capture) {
|
78 | output += str;
|
79 | } else {
|
80 | process.stdout.write(str);
|
81 | }
|
82 | }
|
83 |
|
84 | colorize(str, clazz) {
|
85 | const colors = {
|
86 | low: '31;1',
|
87 | medium: '33;1',
|
88 | high: '32;1'
|
89 | };
|
90 |
|
91 | /* istanbul ignore next: different modes for CI and local */
|
92 | if (supportsColor.stdout && colors[clazz]) {
|
93 | return `\u001b[${colors[clazz]}m${str}\u001b[0m`;
|
94 | }
|
95 | return str;
|
96 | }
|
97 | }
|
98 |
|
99 | /**
|
100 | * utility for writing files under a specific directory
|
101 | * @class FileWriter
|
102 | * @param {String} baseDir the base directory under which files should be written
|
103 | * @constructor
|
104 | */
|
105 | class FileWriter {
|
106 | constructor(baseDir) {
|
107 | if (!baseDir) {
|
108 | throw new Error('baseDir must be specified');
|
109 | }
|
110 | this.baseDir = baseDir;
|
111 | }
|
112 |
|
113 | /**
|
114 | * static helpers for capturing stdout report output;
|
115 | * super useful for tests!
|
116 | */
|
117 | static startCapture() {
|
118 | capture = true;
|
119 | }
|
120 |
|
121 | static stopCapture() {
|
122 | capture = false;
|
123 | }
|
124 |
|
125 | static getOutput() {
|
126 | return output;
|
127 | }
|
128 |
|
129 | static resetOutput() {
|
130 | output = '';
|
131 | }
|
132 |
|
133 | /**
|
134 | * returns a FileWriter that is rooted at the supplied subdirectory
|
135 | * @param {String} subdir the subdirectory under which to root the
|
136 | * returned FileWriter
|
137 | * @returns {FileWriter}
|
138 | */
|
139 | writerForDir(subdir) {
|
140 | if (path.isAbsolute(subdir)) {
|
141 | throw new Error(
|
142 | `Cannot create subdir writer for absolute path: ${subdir}`
|
143 | );
|
144 | }
|
145 | return new FileWriter(`${this.baseDir}/${subdir}`);
|
146 | }
|
147 |
|
148 | /**
|
149 | * copies a file from a source directory to a destination name
|
150 | * @param {String} source path to source file
|
151 | * @param {String} dest relative path to destination file
|
152 | * @param {String} [header=undefined] optional text to prepend to destination
|
153 | * (e.g., an "this file is autogenerated" comment, copyright notice, etc.)
|
154 | */
|
155 | copyFile(source, dest, header) {
|
156 | if (path.isAbsolute(dest)) {
|
157 | throw new Error(`Cannot write to absolute path: ${dest}`);
|
158 | }
|
159 | dest = path.resolve(this.baseDir, dest);
|
160 | mkdirp.sync(path.dirname(dest));
|
161 | let contents;
|
162 | if (header) {
|
163 | contents = header + fs.readFileSync(source, 'utf8');
|
164 | } else {
|
165 | contents = fs.readFileSync(source);
|
166 | }
|
167 | fs.writeFileSync(dest, contents);
|
168 | }
|
169 |
|
170 | /**
|
171 | * returns a content writer for writing content to the supplied file.
|
172 | * @param {String|null} file the relative path to the file or the special
|
173 | * values `"-"` or `null` for writing to the console
|
174 | * @returns {ContentWriter}
|
175 | */
|
176 | writeFile(file) {
|
177 | if (file === null || file === '-') {
|
178 | return new ConsoleWriter();
|
179 | }
|
180 | if (path.isAbsolute(file)) {
|
181 | throw new Error(`Cannot write to absolute path: ${file}`);
|
182 | }
|
183 | file = path.resolve(this.baseDir, file);
|
184 | mkdirp.sync(path.dirname(file));
|
185 | return new FileContentWriter(fs.openSync(file, 'w'));
|
186 | }
|
187 | }
|
188 |
|
189 | module.exports = FileWriter;
|