e11ae37c775c06f84b0019cd391ae9af7c3aa694.svn-base
7.15 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.defaultOpts = defaultOpts;
exports.default = void 0;
var parser = _interopRequireWildcard(require("@babel/parser"));
var t = _interopRequireWildcard(require("@babel/types"));
var _traverse = _interopRequireDefault(require("@babel/traverse"));
var _generator = _interopRequireDefault(require("@babel/generator"));
var _visitor = _interopRequireDefault(require("./visitor"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
/*
Copyright 2012-2015, Yahoo Inc.
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
function defaultOpts() {
return {
coverageVariable: '__coverage__',
coverageGlobalScope: 'this',
coverageGlobalScopeFunc: true,
preserveComments: false,
compact: true,
esModules: false,
autoWrap: false,
produceSourceMap: false,
ignoreClassMethods: [],
sourceMapUrlCallback: null,
debug: false,
/* babel parser plugins are to be enabled when the feature is stage 3 and
* implemented in a released version of node.js */
plugins: ['asyncGenerators', 'bigInt', 'classProperties', 'classPrivateProperties', 'dynamicImport', 'importMeta', 'objectRestSpread', 'optionalCatchBinding', 'flow', 'jsx']
};
}
/**
* Instrumenter is the public API for the instrument library.
* It is typically used for ES5 code. For ES6 code that you
* are already running under `babel` use the coverage plugin
* instead.
* @param {Object} opts optional.
* @param {string} [opts.coverageVariable=__coverage__] name of global coverage variable.
* @param {boolean} [opts.preserveComments=false] preserve comments in output
* @param {boolean} [opts.compact=true] generate compact code.
* @param {boolean} [opts.esModules=false] set to true to instrument ES6 modules.
* @param {boolean} [opts.autoWrap=false] set to true to allow `return` statements outside of functions.
* @param {boolean} [opts.produceSourceMap=false] set to true to produce a source map for the instrumented code.
* @param {Array} [opts.ignoreClassMethods=[]] set to array of class method names to ignore for coverage.
* @param {Function} [opts.sourceMapUrlCallback=null] a callback function that is called when a source map URL
* is found in the original code. This function is called with the source file name and the source map URL.
* @param {boolean} [opts.debug=false] - turn debugging on
* @param {array} [opts.plugins=['asyncGenerators','dynamicImport','objectRestSpread','optionalCatchBinding','flow','jsx']] - set plugins
*/
class Instrumenter {
constructor(opts = defaultOpts()) {
this.opts = this.normalizeOpts(opts);
this.fileCoverage = null;
this.sourceMap = null;
}
/**
* normalize options passed in and assign defaults.
* @param opts
* @private
*/
normalizeOpts(opts) {
const normalize = (name, defaultValue) => {
if (!opts.hasOwnProperty(name)) {
opts[name] = defaultValue;
}
};
const defOpts = defaultOpts();
Object.keys(defOpts).forEach(k => {
normalize(k, defOpts[k]);
});
return opts;
}
/**
* instrument the supplied code and track coverage against the supplied
* filename. It throws if invalid code is passed to it. ES5 and ES6 syntax
* is supported. To instrument ES6 modules, make sure that you set the
* `esModules` property to `true` when creating the instrumenter.
*
* @param {string} code - the code to instrument
* @param {string} filename - the filename against which to track coverage.
* @param {object} [inputSourceMap] - the source map that maps the not instrumented code back to it's original form.
* Is assigned to the coverage object and therefore, is available in the json output and can be used to remap the
* coverage to the untranspiled source.
* @returns {string} the instrumented code.
*/
instrumentSync(code, filename, inputSourceMap) {
if (typeof code !== 'string') {
throw new Error('Code must be a string');
}
filename = filename || String(new Date().getTime()) + '.js';
const opts = this.opts;
const ast = parser.parse(code, {
allowReturnOutsideFunction: opts.autoWrap,
sourceType: opts.esModules ? 'module' : 'script',
plugins: opts.plugins
});
const ee = (0, _visitor.default)(t, filename, {
coverageVariable: opts.coverageVariable,
coverageGlobalScope: opts.coverageGlobalScope,
coverageGlobalScopeFunc: opts.coverageGlobalScopeFunc,
ignoreClassMethods: opts.ignoreClassMethods,
inputSourceMap
});
let output = {};
const visitor = {
Program: {
enter: ee.enter,
exit(path) {
output = ee.exit(path);
}
}
};
(0, _traverse.default)(ast, visitor);
const generateOptions = {
compact: opts.compact,
comments: opts.preserveComments,
sourceMaps: opts.produceSourceMap,
sourceFileName: filename
};
const codeMap = (0, _generator.default)(ast, generateOptions, code);
this.fileCoverage = output.fileCoverage;
this.sourceMap = codeMap.map;
const cb = this.opts.sourceMapUrlCallback;
if (cb && output.sourceMappingURL) {
cb(filename, output.sourceMappingURL);
}
return codeMap.code;
}
/**
* callback-style instrument method that calls back with an error
* as opposed to throwing one. Note that in the current implementation,
* the callback will be called in the same process tick and is not asynchronous.
*
* @param {string} code - the code to instrument
* @param {string} filename - the filename against which to track coverage.
* @param {Function} callback - the callback
* @param {Object} inputSourceMap - the source map that maps the not instrumented code back to it's original form.
* Is assigned to the coverage object and therefore, is available in the json output and can be used to remap the
* coverage to the untranspiled source.
*/
instrument(code, filename, callback, inputSourceMap) {
if (!callback && typeof filename === 'function') {
callback = filename;
filename = null;
}
try {
const out = this.instrumentSync(code, filename, inputSourceMap);
callback(null, out);
} catch (ex) {
callback(ex);
}
}
/**
* returns the file coverage object for the last file instrumented.
* @returns {Object} the file coverage object.
*/
lastFileCoverage() {
return this.fileCoverage;
}
/**
* returns the source map produced for the last file instrumented.
* @returns {null|Object} the source map object.
*/
lastSourceMap() {
return this.sourceMap;
}
}
var _default = Instrumenter;
exports.default = _default;