Skip to content

Commit

Permalink
feat: allow an input source-map to be passed to instrumentSync() (ist…
Browse files Browse the repository at this point in the history
  • Loading branch information
Micha Reiser authored and bcoe committed Nov 10, 2016
1 parent 8e5e132 commit b08e4f5
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 12 deletions.
10 changes: 9 additions & 1 deletion api.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ is supported. To instrument ES6 modules, make sure that you set the

- `code` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the code to instrument
- `filename` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the filename against which to track coverage.
- `inputSourceMap` **\[[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the instrumented code.

Expand All @@ -53,6 +56,9 @@ the callback will be called in the same process tick and is not asynchronous.
- `code` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the code to instrument
- `filename` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the filename against which to track coverage.
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** the callback
- `inputSourceMap` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 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.

## lastFileCoverage

Expand Down Expand Up @@ -84,5 +90,7 @@ The exit function returns an object that currently has the following keys:

- `types` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** an instance of babel-types
- `sourceFilePath` **\[[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)](default 'unknown.js')** the path to source file
- `opts` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)](default {coverageVariable: '\_\_coverage\_\_'})** additional options
- `opts` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)](default {coverageVariable: '\_\_coverage\_\_', inputSourceMap: undefined })** additional options
- `opts.coverageVariable` **\[[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** the global coverage variable name. (optional, default `__coverage__`)
- `opts.inputSourceMap` **\[[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** the input source map, that maps the uninstrumented code back to the
original code. (optional, default `undefined`)
15 changes: 11 additions & 4 deletions src/instrumenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,12 @@ class 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) {
instrumentSync(code, filename, inputSourceMap) {
if (typeof code !== 'string') {
throw new Error('Code must be a string');
}
Expand All @@ -80,7 +83,8 @@ class Instrumenter {
sourceType: opts.esModules ? "module" : "script"
});
const ee = programVisitor(t, filename, {
coverageVariable: opts.coverageVariable
coverageVariable: opts.coverageVariable,
inputSourceMap: inputSourceMap
});
let output = {};
const visitor = {
Expand Down Expand Up @@ -115,14 +119,17 @@ class Instrumenter {
* @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) {
instrument(code, filename, callback, inputSourceMap) {
if (!callback && typeof filename === 'function') {
callback = filename;
filename = null;
}
try {
var out = this.instrumentSync(code, filename);
var out = this.instrumentSync(code, filename, inputSourceMap);
callback(null, out);
} catch (ex) {
callback(ex);
Expand Down
9 changes: 9 additions & 0 deletions src/source-coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ class SourceCoverage extends classes.FileCoverage {
return counts.length - 1;
}

/**
* Assigns an input source map to the coverage that can be used
* to remap the coverage output to the original source
* @param sourceMap {object} the source map
*/
inputSourceMap(sourceMap) {
this.data.inputSourceMap = sourceMap;
}

freeze() {
// prune empty branches
var map = this.data.branchMap,
Expand Down
12 changes: 9 additions & 3 deletions src/visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ function genVar(filename) {
// VisitState holds the state of the visitor, provides helper functions
// and is the `this` for the individual coverage visitors.
class VisitState {
constructor(types, sourceFilePath) {
constructor(types, sourceFilePath, inputSourceMap) {
this.varName = genVar(sourceFilePath);
this.attrs = {};
this.nextIgnore = null;
this.cov = new SourceCoverage(sourceFilePath);

if (typeof (inputSourceMap) !== "undefined") {
this.cov.inputSourceMap(inputSourceMap);
}
this.types = types;
this.sourceMappingURL = null;
}
Expand Down Expand Up @@ -453,10 +457,12 @@ const coverageTemplate = template(`
* @param {string} sourceFilePath - the path to source file
* @param {Object} opts - additional options
* @param {string} [opts.coverageVariable=__coverage__] the global coverage variable name.
* @param {object} [opts.inputSourceMap=undefined] the input source map, that maps the uninstrumented code back to the
* original code.
*/
function programVisitor(types, sourceFilePath = 'unknown.js', opts = {coverageVariable: '__coverage__'}) {
function programVisitor(types, sourceFilePath = 'unknown.js', opts = {coverageVariable: '__coverage__', inputSourceMap: undefined }) {
const T = types;
const visitState = new VisitState(types, sourceFilePath);
const visitState = new VisitState(types, sourceFilePath, opts.inputSourceMap);
return {
enter(path) {
path.traverse(codeVisitor, visitState);
Expand Down
2 changes: 1 addition & 1 deletion test/specs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function generateTests(docs) {
(doc.tests || []).forEach(function (t) {
var fn = function () {
var genOnly = (doc.opts || {}).generateOnly,
v = verifier.create(doc.code, doc.opts || {}, doc.instrumentOpts),
v = verifier.create(doc.code, doc.opts || {}, doc.instrumentOpts, doc.inputSourceMap),
test = clone(t),
args = test.args,
out = test.out;
Expand Down
24 changes: 24 additions & 0 deletions test/specs/input-source-map.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: defined input source map
code: |
output = "test"
inputSourceMap: { file: "test.js", mappings: "", names: [], sourceRoot: undefined, sources: [ "test.js" ], sourcesContent: [ 'output = "test"' ], version: 3 }
tests:
- name: sets the input source map
args: []
out: "test"
lines: { '1': 1 }
statements: { '0': 1 }
inputSourceMap: { file: "test.js", mappings: "", names: [], sourceRoot: undefined, sources: [ "test.js" ], sourcesContent: [ 'output = "test"' ], version: 3 }
---
name: without input source map
code: |
output = "test"
inputSourceMap: undefined
tests:
- name: is not set on the coverage object
args: []
out: "test"
lines: { '1': 1 }
statements: { '0': 1 }
inputSourceMap: undefined
8 changes: 5 additions & 3 deletions test/util/verifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class Verifier {
assert.deepEqual(cov.f, expectedCoverage.functions || {}, 'Function coverage mismatch');
assert.deepEqual(cov.b, expectedCoverage.branches || {}, 'Branch coverage mismatch');
assert.deepEqual(cov.s, expectedCoverage.statements || {}, 'Statement coverage mismatch');
const initial = readInitialCoverage(this.getGeneratedCode());
assert.deepEqual(cov.data.inputSourceMap, expectedCoverage.inputSourceMap || undefined, "Input source map mismatch");
const initial = readInitialCoverage(this.getGeneratedCode());
assert.ok(initial);
assert.deepEqual(initial.coverageData, this.result.emptyCoverage);
assert.ok(initial.path);
Expand All @@ -57,6 +58,7 @@ class Verifier {
}
assert.equal(initial.gcv, this.result.coverageVariable);
assert.ok(initial.hash);

}

getCoverage() {
Expand Down Expand Up @@ -85,7 +87,7 @@ function extractTestOption(opts, name, defaultValue) {
return v;
}

function create(code, opts, instrumenterOpts) {
function create(code, opts, instrumenterOpts, inputSourceMap) {

opts = opts || {};
instrumenterOpts = instrumenterOpts || {};
Expand All @@ -108,7 +110,7 @@ function create(code, opts, instrumenterOpts) {
}
instrumenter = new Instrumenter(instrumenterOpts);
try {
instrumenterOutput = instrumenter.instrumentSync(code, file);
instrumenterOutput = instrumenter.instrumentSync(code, file, inputSourceMap);
if (debug) {
console.log('================== Original ============================================');
console.log(annotatedCode(code));
Expand Down

0 comments on commit b08e4f5

Please sign in to comment.