UNPKG

3.8 kBJavaScriptView Raw
1'use strict';
2
3var through = require('through2').obj;
4var path = require('path');
5var istanbul = require('istanbul');
6var gutil = require('gulp-util');
7var _ = require('lodash');
8var hook = istanbul.hook;
9var Report = istanbul.Report;
10var Collector = istanbul.Collector;
11var PluginError = gutil.PluginError;
12
13var PLUGIN_NAME = 'gulp-istanbul';
14var COVERAGE_VARIABLE = '$$cov_' + new Date().getTime() + '$$';
15
16var plugin = module.exports = function (opts) {
17 opts = opts || {};
18 opts.includeUntested = opts.includeUntested === true;
19 if (!opts.coverageVariable) opts.coverageVariable = COVERAGE_VARIABLE;
20 var fileMap = {};
21
22 hook.hookRequire(function (path) {
23 return !!fileMap[path];
24 }, function (code, path) {
25 return fileMap[path];
26 });
27
28 var instrumenter = new istanbul.Instrumenter(opts);
29
30 return through(function (file, enc, cb) {
31 cb = _.once(cb);
32 if (!(file.contents instanceof Buffer)) {
33 return cb(new PluginError(PLUGIN_NAME, 'streams not supported'));
34 }
35
36 instrumenter.instrument(file.contents.toString(), file.path, function (err, code) {
37 if (err) {
38 return cb(new PluginError(
39 PLUGIN_NAME,
40 'Unable to parse ' + file.path + '\n\n' + err.message + '\n'
41 ));
42 }
43
44 file.contents = new Buffer(code);
45
46 // If the file is already required, delete it from the cache otherwise the covered
47 // version will be ignored.
48 delete require.cache[path.resolve(file.path)];
49 fileMap[file.path] = file.contents.toString();
50
51 // Parse the blank coverage object from the instrumented file and save it
52 // to the global coverage variable to enable reporting on non-required
53 // files, a workaround for
54 // https://github.com/gotwarlost/istanbul/issues/112
55 if (opts.includeUntested) {
56 var instrumentedSrc = fileMap[file.path];
57 var covStubRE = /\{.*"path".*"fnMap".*"statementMap".*"branchMap".*\}/g;
58 var covStubMatch = covStubRE.exec(instrumentedSrc);
59 if (covStubMatch !== null) {
60 var covStub = JSON.parse(covStubMatch[0]);
61 global[opts.coverageVariable] = global[opts.coverageVariable] || {};
62 global[opts.coverageVariable][path.resolve(file.path)] = covStub;
63 }
64 }
65
66 return cb(err, file);
67 });
68 });
69};
70
71plugin.summarizeCoverage = function (opts) {
72 opts = opts || {};
73 if (!opts.coverageVariable) opts.coverageVariable = COVERAGE_VARIABLE;
74
75 if (!global[opts.coverageVariable]) throw new Error('no coverage data found, run tests before calling `summarizeCoverage`');
76
77 var collector = new Collector();
78 collector.add(global[opts.coverageVariable]);
79 return istanbul.utils.summarizeCoverage(collector.getFinalCoverage());
80};
81
82plugin.writeReports = function (opts) {
83 if (typeof opts === 'string') opts = { dir: opts };
84 opts = opts || {};
85
86 var defaultDir = path.join(process.cwd(), 'coverage');
87 opts = _.defaults(opts, {
88 coverageVariable: COVERAGE_VARIABLE,
89 dir: defaultDir,
90 reporters: [ 'lcov', 'json', 'text', 'text-summary' ],
91 reportOpts: { dir: opts.dir || defaultDir }
92 });
93
94 var invalid = _.difference(opts.reporters, Report.getReportList());
95 if (invalid.length) {
96 // throw before we start -- fail fast
97 throw new PluginError(PLUGIN_NAME, 'Invalid reporters: ' + invalid.join(', '));
98 }
99
100 var reporters = opts.reporters.map(function (r) {
101 return Report.create(r, _.clone(opts.reportOpts));
102 });
103
104 var cover = through();
105
106 cover.on('end', function () {
107 var collector = new Collector();
108
109 // revert to an object if there are not macthing source files.
110 collector.add(global[opts.coverageVariable] || {});
111
112 reporters.forEach(function (report) {
113 report.writeReport(collector, true);
114 });
115 }).resume();
116
117 return cover;
118};