-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinstrumentIstanbulSystem.js
85 lines (73 loc) · 3.4 KB
/
instrumentIstanbulSystem.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
'use strict';
import Instrumenter from 'istanbul-lib-instrument';
// Good enough ES6 module detection regex.
// Format detections not designed to be accurate, but to handle the 99% use case.
const 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`.
*/
export default function instrumentIstanbulSystem(System, sourceFilePathRegex = /^((?!jspm_packages).)*$/)
{
/* 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.
const coverageVariable = Object.keys(global).filter((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
const instrumenter = Instrumenter.createInstrumenter({ coverageVariable });
// ES6 / ES Modules instrumenter
const instrumenterESM = Instrumenter.createInstrumenter({ coverageVariable, esModules: true });
// Store the original `System.translate` hook.
const systemTranslate = System.translate;
// Override SystemJS translate hook to instrument original sources with Istanbul.
System.translate = (load) =>
{
const 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)
{
const 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);
};
}