aa97017d7056ccb3c4f84ea8af7409bf9f49d9b7.svn-base 5.2 KB
/*
 Copyright 2012-2015, Yahoo Inc.
 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */
const util = require('util');
const path = require('path');
const fs = require('fs');
const mkdirp = require('make-dir');
const supportsColor = require('supports-color');
const isAbsolute =
    path.isAbsolute ||
    /* istanbul ignore next */ function(p) {
        return path.resolve(p) === path.normalize(p);
    };

/**
 * abstract interface for writing content
 * @class ContentWriter
 * @constructor
 */
/* istanbul ignore next: abstract class */
function ContentWriter() {}

/**
 * writes a string as-is to the destination
 * @param {String} str the string to write
 */
/* istanbul ignore next: abstract class */
ContentWriter.prototype.write = function() {
    throw new Error('write: must be overridden');
};

/**
 * 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
 */
ContentWriter.prototype.colorize = function(str /*, clazz*/) {
    return str;
};

/**
 * writes a string appended with a newline to the destination
 * @param {String} str the string to write
 */
ContentWriter.prototype.println = function(str) {
    this.write(str + '\n');
};

/**
 * closes this content writer. Should be called after all writes are complete.
 */
ContentWriter.prototype.close = function() {};

/**
 * a content writer that writes to a file
 * @param {Number} fd - the file descriptor
 * @extends ContentWriter
 * @constructor
 */
function FileContentWriter(fd) {
    this.fd = fd;
}
util.inherits(FileContentWriter, ContentWriter);

FileContentWriter.prototype.write = function(str) {
    fs.writeSync(this.fd, str);
};

FileContentWriter.prototype.close = function() {
    fs.closeSync(this.fd);
};

/**
 * a content writer that writes to the console
 * @extends ContentWriter
 * @constructor
 */
function ConsoleWriter() {}
util.inherits(ConsoleWriter, ContentWriter);

// allow stdout to be captured for tests.
let capture = false;
let output = '';
ConsoleWriter.prototype.write = function(str) {
    if (capture) {
        output += str;
    } else {
        process.stdout.write(str);
    }
};

ConsoleWriter.prototype.colorize = function(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
 */
function FileWriter(baseDir) {
    if (!baseDir) {
        throw new Error('baseDir must be specified');
    }
    this.baseDir = baseDir;
}

/**
 * static helpers for capturing stdout report output;
 * super useful for tests!
 */
FileWriter.startCapture = function() {
    capture = true;
};
FileWriter.stopCapture = function() {
    capture = false;
};
FileWriter.getOutput = function() {
    return output;
};
FileWriter.resetOutput = function() {
    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}
 */
FileWriter.prototype.writerForDir = function(subdir) {
    if (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.)
 */
FileWriter.prototype.copyFile = function(source, dest, header) {
    if (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}
 */
FileWriter.prototype.writeFile = function(file) {
    if (file === null || file === '-') {
        return new ConsoleWriter();
    }
    if (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;