typhonjs-istanbul-instrument-jspm
Version:
Provides a NPM module to add Istanbul instrumentation to JSPM / SystemJS by replacing the System.translate hook.
85 lines (69 loc) • 3.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = instrumentIstanbulSystem;
var _istanbulLibInstrument = require('istanbul-lib-instrument');
var _istanbulLibInstrument2 = _interopRequireDefault(_istanbulLibInstrument);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Good enough ES6 module detection regex.
// Format detections not designed to be accurate, but to handle the 99% use case.
var s_ESM_REGEX = /(^\s*|[}\);\n]\s*)(import\s*(['"]|(\*\s+as\s+)?[^"'\(\)\n;]+\s*from\s*['"]|\{)|export\s+\*\s+from\s+["']|export\s* (\{|default|function|class|var|const|let|async\s+function))/;
/**
* Instruments JSPM / SystemJS replacing the `System.translate` hook adding Istanbul instrumentation of loaded source
* code before deferring the instrumented source for translation. The instrumentation process occurs on the original
* source code and supports ES5 and ES Modules (ES6+).
*
* @param {object} System - An instance of SystemJS.
*
* @param {RegExp} sourceFilePathRegex - A regex which defines which source files are instrumented; default excludes
* any sources with file paths that includes `jspm_packages`.
*/
function instrumentIstanbulSystem(System) {
var sourceFilePathRegex = arguments.length <= 1 || arguments[1] === undefined ? /^((?!jspm_packages).)*$/ : arguments[1];
/* istanbul ignore if */
if (typeof System.translate !== 'function') {
throw new TypeError("instrumentIstanbulSystem - 'System' is not an instance of the SystemJS API.");
}
/* istanbul ignore if */
if (!(sourceFilePathRegex instanceof RegExp)) {
throw new TypeError("instrumentIstanbulSystem - 'sourceFilePathRegex' is not an instance of RexExp.");
}
// Coverage variable created by Istanbul and stored in global variables.
var coverageVariable = Object.keys(global).filter(function (key) {
return key.startsWith('$$cov_');
})[0];
/* istanbul ignore if */
if (typeof coverageVariable === 'undefined') {
throw new TypeError('instrumentIstanbulSystem - Istanbul coverage variable could not be located in global variables.');
}
// ES5 instrumenter
var instrumenter = _istanbulLibInstrument2.default.createInstrumenter({ coverageVariable: coverageVariable });
// ES6 / ES Modules instrumenter
var instrumenterESM = _istanbulLibInstrument2.default.createInstrumenter({ coverageVariable: coverageVariable, esModules: true });
// Store the original `System.translate` hook.
var systemTranslate = System.translate;
// Override SystemJS translate hook to instrument original sources with Istanbul.
System.translate = function (load) {
var filePath = load.address.substr(System.baseURL.length);
// Use `sourceFilePathRegex` to test file path for source instrumentation.
if (sourceFilePathRegex.test(filePath)) {
/* istanbul ignore next */
try {
// If a source file passes the ES6 / ES Module regex test then use the ESM instrumenter.
if (s_ESM_REGEX.test(load.source) || load.metadata.format === 'esm') {
load.source = instrumenterESM.instrumentSync(load.source, filePath);
} else {
load.source = instrumenter.instrumentSync(load.source, filePath);
}
} catch (err) {
var newErr = new Error('Unable to instrument \'' + load.name + '\' for Istanbul:\n\t' + err.message);
newErr.stack = 'Unable to instrument \'' + load.name + '\' for istanbul:\n\t' + err.stack;
newErr.originalErr = err.originalErr || err;
throw newErr;
}
}
// Defer to the original `System.translate` hook.
return systemTranslate.call(System, load);
};
}