aboutsummaryrefslogtreecommitdiff
path: root/node_modules/istanbul-lib-report/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/istanbul-lib-report/lib')
-rw-r--r--node_modules/istanbul-lib-report/lib/context.js122
-rw-r--r--node_modules/istanbul-lib-report/lib/file-writer.js186
-rw-r--r--node_modules/istanbul-lib-report/lib/path.js161
-rw-r--r--node_modules/istanbul-lib-report/lib/summarizer.js284
-rw-r--r--node_modules/istanbul-lib-report/lib/tree.js167
-rw-r--r--node_modules/istanbul-lib-report/lib/watermarks.js14
-rw-r--r--node_modules/istanbul-lib-report/lib/xml-writer.js87
7 files changed, 1021 insertions, 0 deletions
diff --git a/node_modules/istanbul-lib-report/lib/context.js b/node_modules/istanbul-lib-report/lib/context.js
new file mode 100644
index 000000000..034a2b107
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/context.js
@@ -0,0 +1,122 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+var FileWriter = require('./file-writer'),
+ XMLWriter = require('./xml-writer'),
+ tree = require('./tree'),
+ watermarks = require('./watermarks'),
+ fs = require('fs');
+
+function defaultSourceLookup(path) {
+ try {
+ return fs.readFileSync(path, 'utf8');
+ } catch (ex) {
+ throw new Error('Unable to lookup source: ' + path + '(' + ex.message + ')');
+ }
+}
+
+function mergeWatermarks(specified, defaults) {
+ specified = specified || {};
+ Object.keys(defaults).forEach(function (k) {
+ var specValue = specified[k];
+ if (!(specValue && Array.isArray(specValue) && specValue.length === 2)) {
+ specified[k] = defaults[k];
+ }
+ });
+ return specified;
+}
+/**
+ * A reporting context that is passed to report implementations
+ * @param {Object} [opts=null] opts options
+ * @param {String} [opts.dir='coverage'] opts.dir the reporting directory
+ * @param {Object} [opts.watermarks=null] opts.watermarks watermarks for
+ * statements, lines, branches and functions
+ * @param {Function} [opts.sourceFinder=fsLookup] opts.sourceFinder a
+ * function that returns source code given a file path. Defaults to
+ * filesystem lookups based on path.
+ * @constructor
+ */
+function Context(opts) {
+ opts = opts || {};
+ this.dir = opts.dir || 'coverage';
+ this.watermarks = mergeWatermarks(opts.watermarks, watermarks.getDefault());
+ this.sourceFinder = opts.sourceFinder || defaultSourceLookup;
+ this.data = {};
+}
+
+Object.defineProperty(Context.prototype, 'writer', {
+ enumerable: true,
+ get: function () {
+ if (!this.data.writer) {
+ this.data.writer = new FileWriter(this.dir);
+ }
+ return this.data.writer;
+ }
+});
+
+/**
+ * returns a FileWriter implementation for reporting use. Also available
+ * as the `writer` property on the context.
+ * @returns {Writer}
+ */
+Context.prototype.getWriter = function () {
+ return this.writer;
+};
+
+/**
+ * returns the source code for the specified file path or throws if
+ * the source could not be found.
+ * @param {String} filePath the file path as found in a file coverage object
+ * @returns {String} the source code
+ */
+Context.prototype.getSource = function (filePath) {
+ return this.sourceFinder(filePath);
+};
+
+/**
+ * returns the coverage class given a coverage
+ * types and a percentage value.
+ * @param {String} type - the coverage type, one of `statements`, `functions`,
+ * `branches`, or `lines`
+ * @param {Number} value - the percentage value
+ * @returns {String} one of `high`, `medium` or `low`
+ */
+Context.prototype.classForPercent = function (type, value) {
+ var watermarks = this.watermarks[type];
+ if (!watermarks) {
+ return 'unknown';
+ }
+ if (value < watermarks[0]) {
+ return 'low';
+ }
+ if (value >= watermarks[1]) {
+ return 'high';
+ }
+ return 'medium';
+};
+/**
+ * returns an XML writer for the supplied content writer
+ * @param {ContentWriter} contentWriter the content writer to which the returned XML writer
+ * writes data
+ * @returns {XMLWriter}
+ */
+Context.prototype.getXMLWriter = function (contentWriter) {
+ return new XMLWriter(contentWriter);
+};
+/**
+ * returns a full visitor given a partial one.
+ * @param {Object} partialVisitor a partial visitor only having the functions of
+ * interest to the caller. These functions are called with a scope that is the
+ * supplied object.
+ * @returns {Visitor}
+ */
+Context.prototype.getVisitor = function (partialVisitor) {
+ return new tree.Visitor(partialVisitor);
+};
+
+module.exports = {
+ create: function (opts) {
+ return new Context(opts);
+ }
+};
diff --git a/node_modules/istanbul-lib-report/lib/file-writer.js b/node_modules/istanbul-lib-report/lib/file-writer.js
new file mode 100644
index 000000000..3121fbfb6
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/file-writer.js
@@ -0,0 +1,186 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+var util = require('util'),
+ path = require('path'),
+ fs = require('fs'),
+ mkdirp = require('mkdirp'),
+ supportsColor = require('supports-color'),
+ 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.
+var capture = false;
+var output = '';
+ConsoleWriter.prototype.write = function (str) {
+ if (capture) {
+ output += str;
+ } else {
+ process.stdout.write(str);
+ }
+};
+
+ConsoleWriter.prototype.colorize = function (str, clazz) {
+ var colors = {
+ low: '31;1',
+ medium: '33;1',
+ high: '32;1'
+ };
+
+ /* istanbul ignore next: different modes for CI and local */
+ if (supportsColor && 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');
+ }
+ mkdirp.sync(baseDir);
+ 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
+ */
+FileWriter.prototype.copyFile = function (source, dest) {
+ if (isAbsolute(dest)) {
+ throw new Error('Cannot write to absolute path: ' + dest);
+ }
+ dest = path.resolve(this.baseDir, dest);
+ mkdirp.sync(path.dirname(dest));
+ fs.writeFileSync(dest, fs.readFileSync(source));
+};
+/**
+ * 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;
diff --git a/node_modules/istanbul-lib-report/lib/path.js b/node_modules/istanbul-lib-report/lib/path.js
new file mode 100644
index 000000000..9d97e15e3
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/path.js
@@ -0,0 +1,161 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+"use strict";
+
+var path = require('path'),
+ parsePath = require('path-parse'),
+ SEP = path.sep || /* istanbul ignore next */ '/',
+ origParser = parsePath,
+ origSep = SEP;
+
+function makeRelativeNormalizedPath(str, sep) {
+ var parsed = parsePath(str),
+ root = parsed.root,
+ dir,
+ file = parsed.base,
+ quoted,
+ pos;
+
+ // handle a weird windows case separately
+ if (sep === '\\') {
+ pos = root.indexOf(':\\');
+ if (pos >= 0) {
+ root = root.substring(0, pos + 2);
+ }
+ }
+ dir = parsed.dir.substring(root.length);
+
+ if (str === '') {
+ return [];
+ }
+
+ if (sep !== '/') {
+ quoted = new RegExp(sep.replace(/\W/g, '\\$&'), 'g');
+ dir = dir.replace(quoted, '/');
+ file = file.replace(quoted, '/'); // excessively paranoid?
+ }
+
+ if (dir !== '') {
+ dir = dir + '/' + file;
+ } else {
+ dir = file;
+ }
+ if (dir.substring(0,1) === '/') {
+ dir = dir.substring(1);
+ }
+ dir = dir.split(/\/+/);
+ return dir;
+}
+
+function Path(strOrArray) {
+ if (Array.isArray(strOrArray)) {
+ this.v = strOrArray;
+ } else if (typeof strOrArray === "string") {
+ this.v = makeRelativeNormalizedPath(strOrArray, SEP);
+ } else {
+ throw new Error('Invalid Path argument must be string or array:' + strOrArray);
+ }
+}
+
+Path.prototype.toString = function () {
+ return this.v.join('/');
+};
+
+Path.prototype.hasParent = function () {
+ return this.v.length > 0;
+};
+
+Path.prototype.parent = function () {
+ if (!this.hasParent()) {
+ throw new Error('Unable to get parent for 0 elem path');
+ }
+ var p = this.v.slice();
+ p.pop();
+ return new Path(p);
+};
+
+Path.prototype.elements = function () {
+ return this.v.slice();
+};
+
+Path.prototype.contains = function (other) {
+ var i;
+ if (other.length > this.length) {
+ return false;
+ }
+ for (i = 0; i < other.length; i += 1) {
+ if (this.v[i] !== other.v[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+Path.prototype.ancestorOf = function (other) {
+ return other.contains(this) && other.length !== this.length;
+};
+
+Path.prototype.descendantOf = function (other) {
+ return this.contains(other) && other.length !== this.length;
+};
+
+Path.prototype.commonPrefixPath = function (other) {
+ var len = this.length > other.length ? other.length : this.length,
+ i,
+ ret = [];
+
+ for (i = 0; i < len; i +=1 ) {
+ if (this.v[i] === other.v[i]) {
+ ret.push(this.v[i]);
+ } else {
+ break;
+ }
+ }
+ return new Path(ret);
+};
+
+['push', 'pop', 'shift', 'unshift', 'splice'].forEach(function (f) {
+ Path.prototype[f] = function () {
+ var args = Array.prototype.slice.call(arguments),
+ v = this.v;
+ return v[f].apply(v, args);
+ };
+});
+
+Path.compare = function (a, b) {
+ var al = a.length,
+ bl = b.length,
+ astr,
+ bstr;
+ if (al < bl) {
+ return -1;
+ }
+ if (al > bl) {
+ return 1;
+ }
+ astr = a.toString();
+ bstr = b.toString();
+ return astr < bstr ? -1 : astr > bstr ? 1 : 0;
+};
+
+Object.defineProperty(Path.prototype, 'length', {
+ enumerable: true,
+ get: function () {
+ return this.v.length;
+ }
+});
+
+module.exports = Path;
+Path.tester = {
+ setParserAndSep: function (p, sep) {
+ parsePath = p;
+ SEP = sep;
+ },
+ reset: function () {
+ parsePath = origParser;
+ SEP = origSep;
+ }
+};
+
diff --git a/node_modules/istanbul-lib-report/lib/summarizer.js b/node_modules/istanbul-lib-report/lib/summarizer.js
new file mode 100644
index 000000000..ee353b317
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/summarizer.js
@@ -0,0 +1,284 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+"use strict";
+
+var Path = require('./path'),
+ util = require('util'),
+ tree = require('./tree'),
+ coverage = require('istanbul-lib-coverage'),
+ BaseNode = tree.Node,
+ BaseTree = tree.Tree;
+
+function ReportNode(path, fileCoverage) {
+ this.path = path;
+ this.parent = null;
+ this.fileCoverage = fileCoverage;
+ this.children = [];
+}
+
+util.inherits(ReportNode, BaseNode);
+
+ReportNode.prototype.addChild = function (child) {
+ child.parent = this;
+ this.children.push(child);
+};
+
+ReportNode.prototype.asRelative = function (p) {
+ /* istanbul ignore if */
+ if (p.substring(0,1) === '/') {
+ return p.substring(1);
+ }
+ return p;
+};
+
+ReportNode.prototype.getQualifiedName = function () {
+ return this.asRelative(this.path.toString());
+};
+
+ReportNode.prototype.getRelativeName = function () {
+ var parent = this.getParent(),
+ myPath = this.path,
+ relPath,
+ i,
+ parentPath = parent ? parent.path : new Path([]);
+ if (parentPath.ancestorOf(myPath)) {
+ relPath = new Path(myPath.elements());
+ for (i = 0; i < parentPath.length; i += 1) {
+ relPath.shift();
+ }
+ return this.asRelative(relPath.toString());
+ }
+ return this.asRelative(this.path.toString());
+};
+
+ReportNode.prototype.getParent = function () {
+ return this.parent;
+};
+
+ReportNode.prototype.getChildren = function () {
+ return this.children;
+};
+
+ReportNode.prototype.isSummary = function () {
+ return !this.fileCoverage;
+};
+
+ReportNode.prototype.getFileCoverage = function () {
+ return this.fileCoverage;
+};
+
+ReportNode.prototype.getCoverageSummary = function (filesOnly) {
+ var cacheProp = 'c_' + (filesOnly ? 'files' : 'full'),
+ summary;
+
+ if (this.hasOwnProperty(cacheProp)) {
+ return this[cacheProp];
+ }
+
+ if (!this.isSummary()) {
+ summary = this.getFileCoverage().toSummary();
+ } else {
+ var count = 0;
+ summary = coverage.createCoverageSummary();
+ this.getChildren().forEach(function (child) {
+ if (filesOnly && child.isSummary()) {
+ return;
+ }
+ count += 1;
+ summary.merge(child.getCoverageSummary(filesOnly));
+ });
+ if (count === 0 && filesOnly) {
+ summary = null;
+ }
+ }
+ this[cacheProp] = summary;
+ return summary;
+};
+
+function treeFor(root, childPrefix) {
+ var tree = new BaseTree(),
+ visitor,
+ maybePrefix = function (node) {
+ if (childPrefix && !node.isRoot()) {
+ node.path.unshift(childPrefix);
+ }
+ };
+ tree.getRoot = function () {
+ return root;
+ };
+ visitor = {
+ onDetail: function (node) {
+ maybePrefix(node);
+ },
+ onSummary: function (node) {
+ maybePrefix(node);
+ node.children.sort(function (a, b) {
+ var astr = a.path.toString(),
+ bstr = b.path.toString();
+ return astr < bstr ? -1 : astr > bstr ? 1: /* istanbul ignore next */ 0;
+ });
+ }
+ };
+ tree.visit(visitor);
+ return tree;
+}
+
+function findCommonParent(paths) {
+ if (paths.length === 0) {
+ return new Path([]);
+ }
+ var common = paths[0],
+ i;
+
+ for (i = 1; i < paths.length; i += 1) {
+ common = common.commonPrefixPath(paths[i]);
+ if (common.length === 0) {
+ break;
+ }
+ }
+ return common;
+}
+
+function toInitialList(coverageMap) {
+ var ret = [],
+ commonParent;
+ coverageMap.files().forEach(function (filePath) {
+ var p = new Path(filePath),
+ coverage = coverageMap.fileCoverageFor(filePath);
+ ret.push({
+ filePath: filePath,
+ path: p,
+ fileCoverage: coverage
+ });
+ });
+ commonParent = findCommonParent(ret.map(function (o) { return o.path.parent(); }));
+ if (commonParent.length > 0) {
+ ret.forEach(function (o) {
+ o.path.splice(0, commonParent.length);
+ });
+ }
+ return {
+ list: ret,
+ commonParent: commonParent
+ };
+}
+
+function toDirParents(list) {
+ var nodeMap = {},
+ parentNodeList = [];
+ list.forEach(function (o) {
+ var node = new ReportNode(o.path, o.fileCoverage),
+ parentPath = o.path.parent(),
+ parent = nodeMap[parentPath.toString()];
+
+ if (!parent) {
+ parent = new ReportNode(parentPath);
+ nodeMap[parentPath.toString()] = parent;
+ parentNodeList.push(parent);
+ }
+ parent.addChild(node);
+ });
+ return parentNodeList;
+}
+
+function foldIntoParents(nodeList) {
+ var ret = [], i, j;
+
+ // sort by longest length first
+ nodeList.sort(function (a, b) {
+ return -1 * Path.compare(a.path , b.path);
+ });
+
+ for (i = 0; i < nodeList.length; i += 1) {
+ var first = nodeList[i],
+ inserted = false;
+
+ for (j = i + 1; j < nodeList.length; j += 1) {
+ var second = nodeList[j];
+ if (second.path.ancestorOf(first.path)) {
+ second.addChild(first);
+ inserted = true;
+ break;
+ }
+ }
+
+ if (!inserted) {
+ ret.push(first);
+ }
+ }
+ return ret;
+}
+
+function createRoot() {
+ return new ReportNode(new Path([]));
+}
+
+function createNestedSummary(coverageMap) {
+ var flattened = toInitialList(coverageMap),
+ dirParents = toDirParents(flattened.list),
+ topNodes = foldIntoParents(dirParents),
+ root;
+
+ if (topNodes.length === 0) {
+ return treeFor(new ReportNode([]));
+ }
+
+ if (topNodes.length === 1) {
+ return treeFor(topNodes[0]);
+ }
+
+ root = createRoot();
+ topNodes.forEach(function (node) {
+ root.addChild(node);
+ });
+ return treeFor(root);
+}
+
+function createPackageSummary(coverageMap) {
+ var flattened = toInitialList(coverageMap),
+ dirParents = toDirParents(flattened.list),
+ common = flattened.commonParent,
+ prefix,
+ root;
+
+ if (dirParents.length === 1) {
+ root = dirParents[0];
+ } else {
+ root = createRoot();
+ // if one of the dirs is itself the root,
+ // then we need to create a top-level dir
+ dirParents.forEach(function (dp) {
+ if (dp.path.length === 0) {
+ prefix = 'root';
+ }
+ });
+ if (prefix && common.length > 0) {
+ prefix = common.elements()[common.elements().length - 1];
+ }
+ dirParents.forEach(function (node) {
+ root.addChild(node);
+ });
+ }
+ return treeFor(root, prefix);
+}
+
+function createFlatSummary(coverageMap) {
+ var flattened = toInitialList(coverageMap),
+ list = flattened.list,
+ root;
+
+ root = createRoot();
+ list.forEach(function (o) {
+ var node = new ReportNode(o.path, o.fileCoverage);
+ root.addChild(node);
+ });
+ return treeFor(root);
+}
+
+module.exports = {
+ createNestedSummary: createNestedSummary,
+ createPackageSummary: createPackageSummary,
+ createFlatSummary: createFlatSummary
+};
diff --git a/node_modules/istanbul-lib-report/lib/tree.js b/node_modules/istanbul-lib-report/lib/tree.js
new file mode 100644
index 000000000..190808d18
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/tree.js
@@ -0,0 +1,167 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+"use strict";
+
+var util = require('util');
+/**
+ * An object with methods that are called during the traversal of the coverage tree.
+ * A visitor has the following methods that are called during tree traversal.
+ *
+ * * `onStart(root, state)` - called before traversal begins
+ * * `onSummary(node, state)` - called for every summary node
+ * * `onDetail(node, state)` - called for every detail node
+ * * `onSummaryEnd(node, state)` - called after all children have been visited for
+ * a summary node.
+ * * `onEnd(root, state)` - called after traversal ends
+ *
+ * @param delegate - a partial visitor that only implements the methods of interest
+ * The visitor object supplies the missing methods as noops. For example, reports
+ * that only need the final coverage summary need implement `onStart` and nothing
+ * else. Reports that use only detailed coverage information need implement `onDetail`
+ * and nothing else.
+ * @constructor
+ */
+function Visitor(delegate) {
+ this.delegate = delegate;
+}
+
+['Start', 'End', 'Summary', 'SummaryEnd', 'Detail' ].forEach(function (k) {
+ var f = 'on' + k;
+ Visitor.prototype[f] = function (node, state) {
+ if (this.delegate[f] && typeof this.delegate[f] === 'function') {
+ this.delegate[f].call(this.delegate, node, state);
+ }
+ };
+});
+
+function CompositeVisitor(visitors) {
+ if (!Array.isArray(visitors)) {
+ visitors = [visitors];
+ }
+ this.visitors = visitors.map(function (v) {
+ if (v instanceof Visitor) {
+ return v;
+ }
+ return new Visitor(v);
+ });
+}
+
+util.inherits(CompositeVisitor, Visitor);
+
+['Start', 'Summary', 'SummaryEnd', 'Detail', 'End'].forEach(function (k) {
+ var f = 'on' + k;
+ CompositeVisitor.prototype[f] = function (node, state) {
+ this.visitors.forEach(function (v) {
+ v[f](node, state);
+ });
+ };
+});
+
+function Node() {
+}
+
+/* istanbul ignore next: abstract method */
+Node.prototype.getQualifiedName = function () {
+ throw new Error('getQualifiedName must be overridden');
+};
+
+/* istanbul ignore next: abstract method */
+Node.prototype.getRelativeName = function () {
+ throw new Error('getRelativeName must be overridden');
+};
+
+/* istanbul ignore next: abstract method */
+Node.prototype.isRoot = function () {
+ return !this.getParent();
+};
+
+/* istanbul ignore next: abstract method */
+Node.prototype.getParent = function () {
+ throw new Error('getParent must be overridden');
+};
+
+/* istanbul ignore next: abstract method */
+Node.prototype.getChildren = function () {
+ throw new Error('getChildren must be overridden');
+};
+
+/* istanbul ignore next: abstract method */
+Node.prototype.isSummary = function () {
+ throw new Error('isSummary must be overridden');
+};
+
+/* istanbul ignore next: abstract method */
+Node.prototype.getCoverageSummary = function (/* filesOnly */) {
+ throw new Error('getCoverageSummary must be overridden');
+};
+
+/* istanbul ignore next: abstract method */
+Node.prototype.getFileCoverage = function () {
+ throw new Error('getFileCoverage must be overridden');
+};
+/**
+ * visit all nodes depth-first from this node down. Note that `onStart`
+ * and `onEnd` are never called on the visitor even if the current
+ * node is the root of the tree.
+ * @param visitor a full visitor that is called during tree traversal
+ * @param state optional state that is passed around
+ */
+Node.prototype.visit = function (visitor, state) {
+
+ var that = this,
+ visitChildren = function () {
+ that.getChildren().forEach(function (child) {
+ child.visit(visitor, state);
+ });
+ };
+
+ if (this.isSummary()) {
+ visitor.onSummary(this, state);
+ } else {
+ visitor.onDetail(this, state);
+ }
+
+ visitChildren();
+
+ if (this.isSummary()) {
+ visitor.onSummaryEnd(this, state);
+ }
+};
+
+/**
+ * abstract base class for a coverage tree.
+ * @constructor
+ */
+function Tree() {
+}
+
+/**
+ * returns the root node of the tree
+ */
+/* istanbul ignore next: abstract method */
+Tree.prototype.getRoot = function () {
+ throw new Error('getRoot must be overridden');
+};
+
+/**
+ * visits the tree depth-first with the supplied partial visitor
+ * @param visitor - a potentially partial visitor
+ * @param state - the state to be passed around during tree traversal
+ */
+Tree.prototype.visit = function (visitor, state) {
+ if (!(visitor instanceof Visitor)) {
+ visitor = new Visitor(visitor);
+ }
+ visitor.onStart(this.getRoot(), state);
+ this.getRoot().visit(visitor, state);
+ visitor.onEnd(this.getRoot(), state);
+};
+
+module.exports = {
+ Tree: Tree,
+ Node: Node,
+ Visitor: Visitor,
+ CompositeVisitor: CompositeVisitor
+};
diff --git a/node_modules/istanbul-lib-report/lib/watermarks.js b/node_modules/istanbul-lib-report/lib/watermarks.js
new file mode 100644
index 000000000..a522129f1
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/watermarks.js
@@ -0,0 +1,14 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+module.exports = {
+ getDefault: function () {
+ return {
+ statements: [50, 80],
+ functions: [50, 80],
+ branches: [50, 80],
+ lines: [50, 80]
+ };
+ }
+};
diff --git a/node_modules/istanbul-lib-report/lib/xml-writer.js b/node_modules/istanbul-lib-report/lib/xml-writer.js
new file mode 100644
index 000000000..7f372fb3c
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/xml-writer.js
@@ -0,0 +1,87 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+var INDENT = ' ';
+
+/**
+ * a utility class to produce well-formed, indented XML
+ * @param {ContentWriter} contentWriter the content writer that this utility wraps
+ * @constructor
+ */
+function XMLWriter(contentWriter) {
+ this.cw = contentWriter;
+ this.stack = [];
+}
+
+function attrString(attrs) {
+ if (!attrs) {
+ return '';
+ }
+ var ret = [];
+ Object.keys(attrs).forEach(function (k) {
+ var v = attrs[k];
+ ret.push(k + '="' + v + '"');
+ });
+ return ret.length === 0 ? '' : ' ' + ret.join(' ');
+}
+
+XMLWriter.prototype.indent = function (str) {
+ return this.stack.map(function () { return INDENT; }).join('') + str;
+};
+
+/**
+ * writes the opening XML tag with the supplied attributes
+ * @param {String} name tag name
+ * @param {Object} [attrs=null] attrs attributes for the tag
+ */
+XMLWriter.prototype.openTag = function (name, attrs) {
+ var str = this.indent('<' + name + attrString(attrs) + '>');
+ this.cw.println(str);
+ this.stack.push(name);
+};
+
+/**
+ * closes an open XML tag.
+ * @param {String} name - tag name to close. This must match the writer's
+ * notion of the tag that is currently open.
+ */
+XMLWriter.prototype.closeTag = function (name) {
+ if (this.stack.length === 0) {
+ throw new Error('Attempt to close tag ' + name + ' when not opened');
+ }
+ var stashed = this.stack.pop(),
+ str = '</' + name + '>';
+
+ if (stashed !== name) {
+ throw new Error('Attempt to close tag ' + name + ' when ' + stashed + ' was the one open');
+ }
+ this.cw.println(this.indent(str));
+};
+/**
+ * writes a tag and its value opening and closing it at the same time
+ * @param {String} name tag name
+ * @param {Object} [attrs=null] attrs tag attributes
+ * @param {String} [content=null] content optional tag content
+ */
+XMLWriter.prototype.inlineTag = function (name, attrs, content) {
+ var str = '<' + name + attrString(attrs);
+ if (content) {
+ str += '>' + content + '</' + name + '>';
+ } else {
+ str += '/>';
+ }
+ str = this.indent(str);
+ this.cw.println(str);
+};
+/**
+ * closes all open tags and ends the document
+ */
+XMLWriter.prototype.closeAll = function () {
+ var that = this;
+ this.stack.slice().reverse().forEach(function (name) {
+ that.closeTag(name);
+ });
+};
+
+module.exports = XMLWriter;