You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
4.8 KiB
190 lines
4.8 KiB
2 months ago
|
'use strict';
|
||
|
/*
|
||
|
Copyright 2012-2015, Yahoo Inc.
|
||
|
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
|
||
|
*/
|
||
|
const path = require('path');
|
||
|
const fs = require('fs');
|
||
|
const mkdirp = require('make-dir');
|
||
|
const supportsColor = require('supports-color');
|
||
|
|
||
|
/**
|
||
|
* Base class for writing content
|
||
|
* @class ContentWriter
|
||
|
* @constructor
|
||
|
*/
|
||
|
class ContentWriter {
|
||
|
/**
|
||
|
* returns the colorized version of a string. Typically,
|
||
|
* content writers that write to files will return the
|
||
|
* same string and ones writing to a tty will wrap it in
|
||
|
* appropriate escape sequences.
|
||
|
* @param {String} str the string to colorize
|
||
|
* @param {String} clazz one of `high`, `medium` or `low`
|
||
|
* @returns {String} the colorized form of the string
|
||
|
*/
|
||
|
colorize(str /*, clazz*/) {
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* writes a string appended with a newline to the destination
|
||
|
* @param {String} str the string to write
|
||
|
*/
|
||
|
println(str) {
|
||
|
this.write(`${str}\n`);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* closes this content writer. Should be called after all writes are complete.
|
||
|
*/
|
||
|
close() {}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* a content writer that writes to a file
|
||
|
* @param {Number} fd - the file descriptor
|
||
|
* @extends ContentWriter
|
||
|
* @constructor
|
||
|
*/
|
||
|
class FileContentWriter extends ContentWriter {
|
||
|
constructor(fd) {
|
||
|
super();
|
||
|
|
||
|
this.fd = fd;
|
||
|
}
|
||
|
|
||
|
write(str) {
|
||
|
fs.writeSync(this.fd, str);
|
||
|
}
|
||
|
|
||
|
close() {
|
||
|
fs.closeSync(this.fd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// allow stdout to be captured for tests.
|
||
|
let capture = false;
|
||
|
let output = '';
|
||
|
|
||
|
/**
|
||
|
* a content writer that writes to the console
|
||
|
* @extends ContentWriter
|
||
|
* @constructor
|
||
|
*/
|
||
|
class ConsoleWriter extends ContentWriter {
|
||
|
write(str) {
|
||
|
if (capture) {
|
||
|
output += str;
|
||
|
} else {
|
||
|
process.stdout.write(str);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
colorize(str, clazz) {
|
||
|
const colors = {
|
||
|
low: '31;1',
|
||
|
medium: '33;1',
|
||
|
high: '32;1'
|
||
|
};
|
||
|
|
||
|
/* istanbul ignore next: different modes for CI and local */
|
||
|
if (supportsColor.stdout && colors[clazz]) {
|
||
|
return `\u001b[${colors[clazz]}m${str}\u001b[0m`;
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* utility for writing files under a specific directory
|
||
|
* @class FileWriter
|
||
|
* @param {String} baseDir the base directory under which files should be written
|
||
|
* @constructor
|
||
|
*/
|
||
|
class FileWriter {
|
||
|
constructor(baseDir) {
|
||
|
if (!baseDir) {
|
||
|
throw new Error('baseDir must be specified');
|
||
|
}
|
||
|
this.baseDir = baseDir;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* static helpers for capturing stdout report output;
|
||
|
* super useful for tests!
|
||
|
*/
|
||
|
static startCapture() {
|
||
|
capture = true;
|
||
|
}
|
||
|
|
||
|
static stopCapture() {
|
||
|
capture = false;
|
||
|
}
|
||
|
|
||
|
static getOutput() {
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
static resetOutput() {
|
||
|
output = '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* returns a FileWriter that is rooted at the supplied subdirectory
|
||
|
* @param {String} subdir the subdirectory under which to root the
|
||
|
* returned FileWriter
|
||
|
* @returns {FileWriter}
|
||
|
*/
|
||
|
writerForDir(subdir) {
|
||
|
if (path.isAbsolute(subdir)) {
|
||
|
throw new Error(
|
||
|
`Cannot create subdir writer for absolute path: ${subdir}`
|
||
|
);
|
||
|
}
|
||
|
return new FileWriter(`${this.baseDir}/${subdir}`);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* copies a file from a source directory to a destination name
|
||
|
* @param {String} source path to source file
|
||
|
* @param {String} dest relative path to destination file
|
||
|
* @param {String} [header=undefined] optional text to prepend to destination
|
||
|
* (e.g., an "this file is autogenerated" comment, copyright notice, etc.)
|
||
|
*/
|
||
|
copyFile(source, dest, header) {
|
||
|
if (path.isAbsolute(dest)) {
|
||
|
throw new Error(`Cannot write to absolute path: ${dest}`);
|
||
|
}
|
||
|
dest = path.resolve(this.baseDir, dest);
|
||
|
mkdirp.sync(path.dirname(dest));
|
||
|
let contents;
|
||
|
if (header) {
|
||
|
contents = header + fs.readFileSync(source, 'utf8');
|
||
|
} else {
|
||
|
contents = fs.readFileSync(source);
|
||
|
}
|
||
|
fs.writeFileSync(dest, contents);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* returns a content writer for writing content to the supplied file.
|
||
|
* @param {String|null} file the relative path to the file or the special
|
||
|
* values `"-"` or `null` for writing to the console
|
||
|
* @returns {ContentWriter}
|
||
|
*/
|
||
|
writeFile(file) {
|
||
|
if (file === null || file === '-') {
|
||
|
return new ConsoleWriter();
|
||
|
}
|
||
|
if (path.isAbsolute(file)) {
|
||
|
throw new Error(`Cannot write to absolute path: ${file}`);
|
||
|
}
|
||
|
file = path.resolve(this.baseDir, file);
|
||
|
mkdirp.sync(path.dirname(file));
|
||
|
return new FileContentWriter(fs.openSync(file, 'w'));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = FileWriter;
|