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.js132
-rw-r--r--node_modules/istanbul-lib-report/lib/file-writer.js189
-rw-r--r--node_modules/istanbul-lib-report/lib/path.js169
-rw-r--r--node_modules/istanbul-lib-report/lib/report-base.js16
-rw-r--r--node_modules/istanbul-lib-report/lib/summarizer-factory.js284
-rw-r--r--node_modules/istanbul-lib-report/lib/tree.js137
-rw-r--r--node_modules/istanbul-lib-report/lib/watermarks.js15
-rw-r--r--node_modules/istanbul-lib-report/lib/xml-writer.js90
8 files changed, 1032 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 0000000..fbb30bc
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/context.js
@@ -0,0 +1,132 @@
+'use strict';
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+const fs = require('fs');
+const FileWriter = require('./file-writer');
+const XMLWriter = require('./xml-writer');
+const tree = require('./tree');
+const watermarks = require('./watermarks');
+const SummarizerFactory = require('./summarizer-factory');
+
+function defaultSourceLookup(path) {
+ try {
+ return fs.readFileSync(path, 'utf8');
+ } catch (ex) {
+ throw new Error(`Unable to lookup source: ${path} (${ex.message})`);
+ }
+}
+
+function normalizeWatermarks(specified = {}) {
+ Object.entries(watermarks.getDefault()).forEach(([k, value]) => {
+ const specValue = specified[k];
+ if (!Array.isArray(specValue) || specValue.length !== 2) {
+ specified[k] = value;
+ }
+ });
+
+ 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
+ */
+class Context {
+ constructor(opts) {
+ this.dir = opts.dir || 'coverage';
+ this.watermarks = normalizeWatermarks(opts.watermarks);
+ this.sourceFinder = opts.sourceFinder || defaultSourceLookup;
+ this._summarizerFactory = new SummarizerFactory(
+ opts.coverageMap,
+ opts.defaultSummarizer
+ );
+ this.data = {};
+ }
+
+ /**
+ * returns a FileWriter implementation for reporting use. Also available
+ * as the `writer` property on the context.
+ * @returns {Writer}
+ */
+ getWriter() {
+ 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
+ */
+ getSource(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`
+ */
+ classForPercent(type, value) {
+ const 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}
+ */
+ getXMLWriter(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}
+ */
+ getVisitor(partialVisitor) {
+ return new tree.Visitor(partialVisitor);
+ }
+
+ getTree(name = 'defaultSummarizer') {
+ return this._summarizerFactory[name];
+ }
+}
+
+Object.defineProperty(Context.prototype, 'writer', {
+ enumerable: true,
+ get() {
+ if (!this.data.writer) {
+ this.data.writer = new FileWriter(this.dir);
+ }
+ return this.data.writer;
+ }
+});
+
+module.exports = Context;
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 0000000..de1154b
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/file-writer.js
@@ -0,0 +1,189 @@
+'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;
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 0000000..c928b17
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/path.js
@@ -0,0 +1,169 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+'use strict';
+
+const path = require('path');
+let parsePath = path.parse;
+let SEP = path.sep;
+const origParser = parsePath;
+const origSep = SEP;
+
+function makeRelativeNormalizedPath(str, sep) {
+ const parsed = parsePath(str);
+ let root = parsed.root;
+ let dir;
+ let file = parsed.base;
+ let quoted;
+ let 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;
+}
+
+class Path {
+ constructor(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}`
+ );
+ }
+ }
+
+ toString() {
+ return this.v.join('/');
+ }
+
+ hasParent() {
+ return this.v.length > 0;
+ }
+
+ parent() {
+ if (!this.hasParent()) {
+ throw new Error('Unable to get parent for 0 elem path');
+ }
+ const p = this.v.slice();
+ p.pop();
+ return new Path(p);
+ }
+
+ elements() {
+ return this.v.slice();
+ }
+
+ name() {
+ return this.v.slice(-1)[0];
+ }
+
+ contains(other) {
+ let 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;
+ }
+
+ ancestorOf(other) {
+ return other.contains(this) && other.length !== this.length;
+ }
+
+ descendantOf(other) {
+ return this.contains(other) && other.length !== this.length;
+ }
+
+ commonPrefixPath(other) {
+ const len = this.length > other.length ? other.length : this.length;
+ let i;
+ const 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);
+ }
+
+ static compare(a, b) {
+ const al = a.length;
+ const bl = b.length;
+
+ if (al < bl) {
+ return -1;
+ }
+
+ if (al > bl) {
+ return 1;
+ }
+
+ const astr = a.toString();
+ const bstr = b.toString();
+ return astr < bstr ? -1 : astr > bstr ? 1 : 0;
+ }
+}
+
+['push', 'pop', 'shift', 'unshift', 'splice'].forEach(fn => {
+ Object.defineProperty(Path.prototype, fn, {
+ value(...args) {
+ return this.v[fn](...args);
+ }
+ });
+});
+
+Object.defineProperty(Path.prototype, 'length', {
+ enumerable: true,
+ get() {
+ return this.v.length;
+ }
+});
+
+module.exports = Path;
+Path.tester = {
+ setParserAndSep(p, sep) {
+ parsePath = p;
+ SEP = sep;
+ },
+ reset() {
+ parsePath = origParser;
+ SEP = origSep;
+ }
+};
diff --git a/node_modules/istanbul-lib-report/lib/report-base.js b/node_modules/istanbul-lib-report/lib/report-base.js
new file mode 100644
index 0000000..96de750
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/report-base.js
@@ -0,0 +1,16 @@
+'use strict';
+
+// TODO: switch to class private field when targetting node.js 12
+const _summarizer = Symbol('ReportBase.#summarizer');
+
+class ReportBase {
+ constructor(opts = {}) {
+ this[_summarizer] = opts.summarizer;
+ }
+
+ execute(context) {
+ context.getTree(this[_summarizer]).visit(this, context);
+ }
+}
+
+module.exports = ReportBase;
diff --git a/node_modules/istanbul-lib-report/lib/summarizer-factory.js b/node_modules/istanbul-lib-report/lib/summarizer-factory.js
new file mode 100644
index 0000000..5e8acd9
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/summarizer-factory.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';
+
+const coverage = require('istanbul-lib-coverage');
+const Path = require('./path');
+const { BaseNode, BaseTree } = require('./tree');
+
+class ReportNode extends BaseNode {
+ constructor(path, fileCoverage) {
+ super();
+
+ this.path = path;
+ this.parent = null;
+ this.fileCoverage = fileCoverage;
+ this.children = [];
+ }
+
+ static createRoot(children) {
+ const root = new ReportNode(new Path([]));
+
+ children.forEach(child => {
+ root.addChild(child);
+ });
+
+ return root;
+ }
+
+ addChild(child) {
+ child.parent = this;
+ this.children.push(child);
+ }
+
+ asRelative(p) {
+ if (p.substring(0, 1) === '/') {
+ return p.substring(1);
+ }
+ return p;
+ }
+
+ getQualifiedName() {
+ return this.asRelative(this.path.toString());
+ }
+
+ getRelativeName() {
+ const parent = this.getParent();
+ const myPath = this.path;
+ let relPath;
+ let i;
+ const 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());
+ }
+
+ getParent() {
+ return this.parent;
+ }
+
+ getChildren() {
+ return this.children;
+ }
+
+ isSummary() {
+ return !this.fileCoverage;
+ }
+
+ getFileCoverage() {
+ return this.fileCoverage;
+ }
+
+ getCoverageSummary(filesOnly) {
+ const cacheProp = `c_${filesOnly ? 'files' : 'full'}`;
+ let summary;
+
+ if (Object.prototype.hasOwnProperty.call(this, cacheProp)) {
+ return this[cacheProp];
+ }
+
+ if (!this.isSummary()) {
+ summary = this.getFileCoverage().toSummary();
+ } else {
+ let count = 0;
+ summary = coverage.createCoverageSummary();
+ this.getChildren().forEach(child => {
+ if (filesOnly && child.isSummary()) {
+ return;
+ }
+ count += 1;
+ summary.merge(child.getCoverageSummary(filesOnly));
+ });
+ if (count === 0 && filesOnly) {
+ summary = null;
+ }
+ }
+ this[cacheProp] = summary;
+ return summary;
+ }
+}
+
+class ReportTree extends BaseTree {
+ constructor(root, childPrefix) {
+ super(root);
+
+ const maybePrefix = node => {
+ if (childPrefix && !node.isRoot()) {
+ node.path.unshift(childPrefix);
+ }
+ };
+ this.visit({
+ onDetail: maybePrefix,
+ onSummary(node) {
+ maybePrefix(node);
+ node.children.sort((a, b) => {
+ const astr = a.path.toString();
+ const bstr = b.path.toString();
+ return astr < bstr
+ ? -1
+ : astr > bstr
+ ? 1
+ : /* istanbul ignore next */ 0;
+ });
+ }
+ });
+ }
+}
+
+function findCommonParent(paths) {
+ return paths.reduce(
+ (common, path) => common.commonPrefixPath(path),
+ paths[0] || new Path([])
+ );
+}
+
+function findOrCreateParent(parentPath, nodeMap, created = () => {}) {
+ let parent = nodeMap[parentPath.toString()];
+
+ if (!parent) {
+ parent = new ReportNode(parentPath);
+ nodeMap[parentPath.toString()] = parent;
+ created(parentPath, parent);
+ }
+
+ return parent;
+}
+
+function toDirParents(list) {
+ const nodeMap = Object.create(null);
+ list.forEach(o => {
+ const parent = findOrCreateParent(o.path.parent(), nodeMap);
+ parent.addChild(new ReportNode(o.path, o.fileCoverage));
+ });
+
+ return Object.values(nodeMap);
+}
+
+function addAllPaths(topPaths, nodeMap, path, node) {
+ const parent = findOrCreateParent(
+ path.parent(),
+ nodeMap,
+ (parentPath, parent) => {
+ if (parentPath.hasParent()) {
+ addAllPaths(topPaths, nodeMap, parentPath, parent);
+ } else {
+ topPaths.push(parent);
+ }
+ }
+ );
+
+ parent.addChild(node);
+}
+
+function foldIntoOneDir(node, parent) {
+ const { children } = node;
+ if (children.length === 1 && !children[0].fileCoverage) {
+ children[0].parent = parent;
+ return foldIntoOneDir(children[0], parent);
+ }
+ node.children = children.map(child => foldIntoOneDir(child, node));
+ return node;
+}
+
+function pkgSummaryPrefix(dirParents, commonParent) {
+ if (!dirParents.some(dp => dp.path.length === 0)) {
+ return;
+ }
+
+ if (commonParent.length === 0) {
+ return 'root';
+ }
+
+ return commonParent.name();
+}
+
+class SummarizerFactory {
+ constructor(coverageMap, defaultSummarizer = 'pkg') {
+ this._coverageMap = coverageMap;
+ this._defaultSummarizer = defaultSummarizer;
+ this._initialList = coverageMap.files().map(filePath => ({
+ filePath,
+ path: new Path(filePath),
+ fileCoverage: coverageMap.fileCoverageFor(filePath)
+ }));
+ this._commonParent = findCommonParent(
+ this._initialList.map(o => o.path.parent())
+ );
+ if (this._commonParent.length > 0) {
+ this._initialList.forEach(o => {
+ o.path.splice(0, this._commonParent.length);
+ });
+ }
+ }
+
+ get defaultSummarizer() {
+ return this[this._defaultSummarizer];
+ }
+
+ get flat() {
+ if (!this._flat) {
+ this._flat = new ReportTree(
+ ReportNode.createRoot(
+ this._initialList.map(
+ node => new ReportNode(node.path, node.fileCoverage)
+ )
+ )
+ );
+ }
+
+ return this._flat;
+ }
+
+ _createPkg() {
+ const dirParents = toDirParents(this._initialList);
+ if (dirParents.length === 1) {
+ return new ReportTree(dirParents[0]);
+ }
+
+ return new ReportTree(
+ ReportNode.createRoot(dirParents),
+ pkgSummaryPrefix(dirParents, this._commonParent)
+ );
+ }
+
+ get pkg() {
+ if (!this._pkg) {
+ this._pkg = this._createPkg();
+ }
+
+ return this._pkg;
+ }
+
+ _createNested() {
+ const nodeMap = Object.create(null);
+ const topPaths = [];
+ this._initialList.forEach(o => {
+ const node = new ReportNode(o.path, o.fileCoverage);
+ addAllPaths(topPaths, nodeMap, o.path, node);
+ });
+
+ const topNodes = topPaths.map(node => foldIntoOneDir(node));
+ if (topNodes.length === 1) {
+ return new ReportTree(topNodes[0]);
+ }
+
+ return new ReportTree(ReportNode.createRoot(topNodes));
+ }
+
+ get nested() {
+ if (!this._nested) {
+ this._nested = this._createNested();
+ }
+
+ return this._nested;
+ }
+}
+
+module.exports = SummarizerFactory;
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 0000000..7c18204
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/tree.js
@@ -0,0 +1,137 @@
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+'use strict';
+
+/**
+ * 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
+ */
+class Visitor {
+ constructor(delegate) {
+ this.delegate = delegate;
+ }
+}
+
+['Start', 'End', 'Summary', 'SummaryEnd', 'Detail']
+ .map(k => `on${k}`)
+ .forEach(fn => {
+ Object.defineProperty(Visitor.prototype, fn, {
+ writable: true,
+ value(node, state) {
+ if (typeof this.delegate[fn] === 'function') {
+ this.delegate[fn](node, state);
+ }
+ }
+ });
+ });
+
+class CompositeVisitor extends Visitor {
+ constructor(visitors) {
+ super();
+
+ if (!Array.isArray(visitors)) {
+ visitors = [visitors];
+ }
+ this.visitors = visitors.map(v => {
+ if (v instanceof Visitor) {
+ return v;
+ }
+ return new Visitor(v);
+ });
+ }
+}
+
+['Start', 'Summary', 'SummaryEnd', 'Detail', 'End']
+ .map(k => `on${k}`)
+ .forEach(fn => {
+ Object.defineProperty(CompositeVisitor.prototype, fn, {
+ value(node, state) {
+ this.visitors.forEach(v => {
+ v[fn](node, state);
+ });
+ }
+ });
+ });
+
+class BaseNode {
+ isRoot() {
+ return !this.getParent();
+ }
+
+ /**
+ * 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
+ */
+ visit(visitor, state) {
+ if (this.isSummary()) {
+ visitor.onSummary(this, state);
+ } else {
+ visitor.onDetail(this, state);
+ }
+
+ this.getChildren().forEach(child => {
+ child.visit(visitor, state);
+ });
+
+ if (this.isSummary()) {
+ visitor.onSummaryEnd(this, state);
+ }
+ }
+}
+
+/**
+ * abstract base class for a coverage tree.
+ * @constructor
+ */
+class BaseTree {
+ constructor(root) {
+ this.root = root;
+ }
+
+ /**
+ * returns the root node of the tree
+ */
+ getRoot() {
+ return this.root;
+ }
+
+ /**
+ * 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
+ */
+ visit(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 = {
+ BaseTree,
+ BaseNode,
+ Visitor,
+ 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 0000000..fb76082
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/watermarks.js
@@ -0,0 +1,15 @@
+'use strict';
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+module.exports = {
+ getDefault() {
+ 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 0000000..a32550e
--- /dev/null
+++ b/node_modules/istanbul-lib-report/lib/xml-writer.js
@@ -0,0 +1,90 @@
+'use strict';
+/*
+ Copyright 2012-2015, Yahoo Inc.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+const INDENT = ' ';
+
+function attrString(attrs) {
+ return Object.entries(attrs || {})
+ .map(([k, v]) => ` ${k}="${v}"`)
+ .join('');
+}
+
+/**
+ * a utility class to produce well-formed, indented XML
+ * @param {ContentWriter} contentWriter the content writer that this utility wraps
+ * @constructor
+ */
+class XMLWriter {
+ constructor(contentWriter) {
+ this.cw = contentWriter;
+ this.stack = [];
+ }
+
+ indent(str) {
+ return this.stack.map(() => 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
+ */
+ openTag(name, attrs) {
+ const 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.
+ */
+ closeTag(name) {
+ if (this.stack.length === 0) {
+ throw new Error(`Attempt to close tag ${name} when not opened`);
+ }
+ const stashed = this.stack.pop();
+ const 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
+ */
+ inlineTag(name, attrs, content) {
+ let 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
+ */
+ closeAll() {
+ this.stack
+ .slice()
+ .reverse()
+ .forEach(name => {
+ this.closeTag(name);
+ });
+ }
+}
+
+module.exports = XMLWriter;