-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.js
127 lines (104 loc) · 3.63 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
var bunker = require('bunker'),
Module = require('module').Module,
path = require('path'),
fs = require('fs'),
vm = require('vm');
function CoverageData (filename, bunker) {
this.bunker = bunker;
this.filename = filename;
this.nodes = {};
};
CoverageData.prototype.visit = function(node) {
++(this.nodes[node.id] = this.nodes[node.id] || {node:node, count:0}).count;
};
CoverageData.prototype.missing = function() {
var nodes = this.nodes,
missing = this.bunker.nodes.filter(function(node) {
return !nodes[node.id];
});
return missing;
};
CoverageData.prototype.stats = function() {
var missing = this.missing(),
filedata = fs.readFileSync(this.filename, 'utf8').split('\n');
var seenLines = [],
lines =
missing.sort(function(lhs, rhs) {
return lhs.node[0].start.line < rhs.node[0].start.line ? -1 :
lhs.node[0].start.line > rhs.node[0].start.line ? 1 :
0;
}).filter(function(node) {
var okay = (seenLines.indexOf(node.node[0].start.line) < 0);
if(okay)
seenLines.push(node.node[0].start.line);
return okay;
}).map(function(node, idx, all) {
return {
lineno:node.node[0].start.line + 1,
source:function() { return filedata[node.node[0].start.line]; }
};
});
return {
percentage:(filedata.length-seenLines.length)/filedata.length,
lines:lines,
missing:seenLines.length,
seen:(filedata.length-seenLines.length)
};
};
module.exports.createEnvironment = function(module, filename) {
var req = function(path) {
return Module._load(path, module);
};
req.resolve = function(request) {
return Module._resolveFilename(request, module)[1];
}
req.paths = Module._paths;
req.main = process.mainModule;
req.extensions = Module._extensions;
req.registerExtension = function() {
throw new Error('require.registerExtension() removed. Use ' +
'require.extensions instead.');
}
require.cache = Module._cache;
var ctxt = {};
for(var k in global)
ctxt[k] = global[k];
ctxt.require = req;
ctxt.exports = module.exports;
ctxt.__filename = filename;
ctxt.__dirname = path.dirname(filename);
ctxt.process = process;
ctxt.console = console;
ctxt.module = module;
ctxt.global = ctxt;
return ctxt;
};
module.exports.cover = function(fileRegex) {
var originalRequire = require.extensions['.js'],
coverageData = {},
match = fileRegex instanceof RegExp ?
fileRegex : new RegExp(
fileRegex ? fileRegex.replace(/\//g, '\\/').replace(/\./g, '\\.') : '.*'
, 'g'),
target = this;
require.extensions['.js'] = function(module, filename) {
if(!match.test(filename)) return originalRequire(module, filename);
var context = target.createEnvironment(module, filename),
data = fs.readFileSync(filename, 'utf8'),
bunkerized = bunker(data),
coverage = coverageData[filename] = new CoverageData(filename, bunkerized);
bunkerized.on('node', coverage.visit.bind(coverage));
bunkerized.assign(context);
var wrapper = '(function(ctxt) { with(ctxt) { return '+Module.wrap(bunkerized.compile())+'; } })',
compiledWrapper = vm.runInThisContext(wrapper, filename, true)(context);
var args = [context.exports, context.require, module, filename, context.__dirname];
return compiledWrapper.apply(module.exports, args);
};
var retval = function(ready) {
ready(coverageData);
};
retval.release = function() {
require.extensions['.js'] = originalRequire;
};
return retval;
};