source-map-explorer 2.3.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -7
- package/{dist → bin}/cli.d.ts +1 -1
- package/{dist → bin}/cli.js +59 -93
- package/bin/cli.js.map +1 -0
- package/{dist → lib}/api.d.ts +1 -7
- package/lib/api.js +134 -0
- package/lib/api.js.map +1 -0
- package/{dist → lib}/app-error.d.ts +1 -1
- package/{dist → lib}/app-error.js +14 -34
- package/lib/app-error.js.map +1 -0
- package/{dist → lib}/coverage.d.ts +1 -10
- package/{dist → lib}/coverage.js +28 -125
- package/lib/coverage.js.map +1 -0
- package/{dist → lib}/explore.d.ts +1 -4
- package/lib/explore.js +227 -0
- package/lib/explore.js.map +1 -0
- package/{dist → lib}/helpers.d.ts +2 -16
- package/{dist → lib}/helpers.js +36 -68
- package/lib/helpers.js.map +1 -0
- package/{dist → lib}/html.d.ts +6 -7
- package/lib/html.js +191 -0
- package/lib/html.js.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +31 -0
- package/lib/index.js.map +1 -0
- package/{dist → lib}/output.d.ts +1 -1
- package/{dist → lib}/output.js +22 -53
- package/lib/output.js.map +1 -0
- package/{dist → lib}/tree-viz.ejs +0 -0
- package/lib/types.d.ts +121 -0
- package/{dist → lib}/vendor/webtreemap.css +0 -0
- package/{dist → lib}/vendor/webtreemap.js +0 -0
- package/package.json +72 -72
- package/dist/api.js +0 -168
- package/dist/explore.js +0 -258
- package/dist/html.js +0 -213
- package/dist/index.d.ts +0 -92
- package/dist/index.js +0 -38
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
});
|
|
6
|
-
exports.getErrorMessage = getErrorMessage;
|
|
7
|
-
exports.SOURCE_MAP_INFO_URL = exports.AppError = void 0;
|
|
8
|
-
|
|
9
|
-
var _helpers = require('./helpers');
|
|
10
|
-
|
|
11
|
-
// If we need advanced error consider using https://github.com/joyent/node-verror
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.getErrorMessage = exports.SOURCE_MAP_INFO_URL = exports.AppError = void 0;
|
|
4
|
+
const helpers_1 = require('./helpers');
|
|
12
5
|
class AppError extends Error {
|
|
13
6
|
constructor(errorContext, error) {
|
|
14
|
-
super();
|
|
15
|
-
|
|
7
|
+
super();
|
|
16
8
|
Object.setPrototypeOf(this, AppError.prototype);
|
|
17
9
|
const message = getErrorMessage(errorContext);
|
|
18
10
|
this.message = error ? `${message}: ${error.message}` : message;
|
|
@@ -20,69 +12,57 @@ class AppError extends Error {
|
|
|
20
12
|
Error.captureStackTrace(this, AppError);
|
|
21
13
|
}
|
|
22
14
|
}
|
|
23
|
-
|
|
24
15
|
exports.AppError = AppError;
|
|
25
|
-
|
|
16
|
+
exports.SOURCE_MAP_INFO_URL =
|
|
26
17
|
'https://github.com/danvk/source-map-explorer/blob/master/README.md#generating-source-maps';
|
|
27
|
-
exports.SOURCE_MAP_INFO_URL = SOURCE_MAP_INFO_URL;
|
|
28
|
-
|
|
29
18
|
function getErrorMessage(context) {
|
|
30
19
|
switch (context.code) {
|
|
31
20
|
case 'NoBundles':
|
|
32
21
|
return 'No file(s) provided';
|
|
33
|
-
|
|
34
22
|
case 'NoSourceMap':
|
|
35
23
|
return `Unable to find a source map.
|
|
36
|
-
See ${SOURCE_MAP_INFO_URL}`;
|
|
37
|
-
|
|
24
|
+
See ${exports.SOURCE_MAP_INFO_URL}`;
|
|
38
25
|
case 'OneSourceSourceMap': {
|
|
39
26
|
return `Your source map only contains one source (${context.filename}).
|
|
40
27
|
This can happen if you use browserify+uglifyjs, for example, and don't set the --in-source-map flag to uglify.
|
|
41
|
-
See ${SOURCE_MAP_INFO_URL}`;
|
|
28
|
+
See ${exports.SOURCE_MAP_INFO_URL}`;
|
|
42
29
|
}
|
|
43
|
-
|
|
44
30
|
case 'UnmappedBytes': {
|
|
45
31
|
const { unmappedBytes, totalBytes } = context;
|
|
46
|
-
const bytesString =
|
|
32
|
+
const bytesString = helpers_1.formatPercent(unmappedBytes, totalBytes, 2);
|
|
47
33
|
return `Unable to map ${unmappedBytes}/${totalBytes} bytes (${bytesString}%)`;
|
|
48
34
|
}
|
|
49
|
-
|
|
50
35
|
case 'InvalidMappingLine': {
|
|
51
36
|
const { generatedLine, maxLine } = context;
|
|
52
37
|
return `Your source map refers to generated line ${generatedLine}, but the source only contains ${maxLine} line(s).
|
|
53
38
|
Check that you are using the correct source map.`;
|
|
54
39
|
}
|
|
55
|
-
|
|
56
40
|
case 'InvalidMappingColumn': {
|
|
57
|
-
const { generatedLine, generatedColumn, maxColumn } = context;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
41
|
+
const { generatedLine, generatedColumn, maxColumn } = context;
|
|
42
|
+
return `Your source map refers to generated column ${
|
|
43
|
+
generatedColumn + 1
|
|
44
|
+
} on line ${generatedLine}, but the source only contains ${maxColumn} column(s) on that line.
|
|
61
45
|
Check that you are using the correct source map.`;
|
|
62
46
|
}
|
|
63
|
-
|
|
64
47
|
case 'CannotSaveFile':
|
|
65
48
|
return 'Unable to save HTML to file';
|
|
66
|
-
|
|
67
49
|
case 'CannotCreateTempFile':
|
|
68
50
|
return 'Unable to create a temporary HTML file';
|
|
69
|
-
|
|
70
51
|
case 'CannotOpenTempFile': {
|
|
71
52
|
const { error, tempFile } = context;
|
|
72
53
|
return `Unable to open web browser. ${error.toString().trim()}
|
|
73
54
|
Either run with --html, --json, --tsv, --file, or view HTML for the visualization at:
|
|
74
55
|
${tempFile}`;
|
|
75
56
|
}
|
|
76
|
-
|
|
77
57
|
case 'CannotOpenCoverageFile': {
|
|
78
58
|
return 'Unable to open/parse coverage file';
|
|
79
59
|
}
|
|
80
|
-
|
|
81
60
|
case 'NoCoverageMatches': {
|
|
82
61
|
return 'No matched bundles found for coverages';
|
|
83
62
|
}
|
|
84
|
-
|
|
85
63
|
default:
|
|
86
64
|
return 'Unknown error';
|
|
87
65
|
}
|
|
88
66
|
}
|
|
67
|
+
exports.getErrorMessage = getErrorMessage;
|
|
68
|
+
//# sourceMappingURL=app-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-error.js","sourceRoot":"","sources":["../src/lib/app-error.ts"],"names":[],"mappings":";;;AAAA,uCAA0C;AAK1C,MAAa,QAAS,SAAQ,KAAK;IAIjC,YAAY,YAA0B,EAAE,KAA6B;QACnE,KAAK,EAAE,CAAC;QAER,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QAEhD,MAAM,OAAO,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;QAE9C,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QAChE,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;QAE9B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC1C,CAAC;CACF;AAhBD,4BAgBC;AAEY,QAAA,mBAAmB,GAC9B,2FAA2F,CAAC;AAmD9F,SAAgB,eAAe,CAAC,OAAqB;IACnD,QAAQ,OAAO,CAAC,IAAI,EAAE;QACpB,KAAK,WAAW;YACd,OAAO,qBAAqB,CAAC;QAE/B,KAAK,aAAa;YAChB,OAAO;MACP,2BAAmB,EAAE,CAAC;QAExB,KAAK,oBAAoB,CAAC,CAAC;YACzB,OAAO,6CAA6C,OAAO,CAAC,QAAQ;;MAEpE,2BAAmB,EAAE,CAAC;SACvB;QAED,KAAK,eAAe,CAAC,CAAC;YACpB,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;YAE9C,MAAM,WAAW,GAAG,uBAAa,CAAC,aAAa,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;YAEhE,OAAO,iBAAiB,aAAa,IAAI,UAAU,WAAW,WAAW,IAAI,CAAC;SAC/E;QAED,KAAK,oBAAoB,CAAC,CAAC;YACzB,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;YAE3C,OAAO,4CAA4C,aAAa,kCAAkC,OAAO;iDAC9D,CAAC;SAC7C;QAED,KAAK,sBAAsB,CAAC,CAAC;YAC3B,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;YAG9D,OAAO,8CACL,eAAe,GAAG,CACpB,YAAY,aAAa,kCAAkC,SAAS;iDACzB,CAAC;SAC7C;QAED,KAAK,gBAAgB;YACnB,OAAO,6BAA6B,CAAC;QAEvC,KAAK,sBAAsB;YACzB,OAAO,wCAAwC,CAAC;QAElD,KAAK,oBAAoB,CAAC,CAAC;YACzB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;YAEpC,OAAO,+BAA+B,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE;;EAEjE,QAAQ,EAAE,CAAC;SACR;QAED,KAAK,wBAAwB,CAAC,CAAC;YAC7B,OAAO,oCAAoC,CAAC;SAC7C;QAED,KAAK,mBAAmB,CAAC,CAAC;YACxB,OAAO,wCAAwC,CAAC;SACjD;QAED;YACE,OAAO,eAAe,CAAC;KAC1B;AACH,CAAC;AAjED,0CAiEC","sourcesContent":["import { formatPercent } from './helpers';\n\nimport type { ErrorCode } from './types';\n\n// If we need advanced error consider using https://github.com/joyent/node-verror\nexport class AppError extends Error {\n code?: ErrorCode;\n cause?: Error;\n\n constructor(errorContext: ErrorContext, error?: NodeJS.ErrnoException) {\n super();\n // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work\n Object.setPrototypeOf(this, AppError.prototype);\n\n const message = getErrorMessage(errorContext);\n\n this.message = error ? `${message}: ${error.message}` : message;\n this.code = errorContext.code;\n\n Error.captureStackTrace(this, AppError);\n }\n}\n\nexport const SOURCE_MAP_INFO_URL =\n 'https://github.com/danvk/source-map-explorer/blob/master/README.md#generating-source-maps';\n\ninterface CommonErrorContext {\n code:\n | 'NoBundles'\n | 'NoSourceMap'\n | 'CannotSaveFile'\n | 'CannotCreateTempFile'\n | 'CannotOpenCoverageFile'\n | 'NoCoverageMatches'\n | 'Unknown';\n}\n\ninterface OneSourceSourceMapErrorContext {\n code: 'OneSourceSourceMap';\n filename: string;\n}\n\ninterface UnmappedBytesErrorContext {\n code: 'UnmappedBytes';\n totalBytes: number;\n unmappedBytes: number;\n}\n\ninterface InvalidMappingLineErrorContext {\n code: 'InvalidMappingLine';\n generatedLine: number;\n maxLine: number;\n}\n\ninterface InvalidMappingColumnErrorContext {\n code: 'InvalidMappingColumn';\n generatedLine: number;\n generatedColumn: number;\n maxColumn: number;\n}\n\ninterface CannotOpenTempFileErrorContext {\n code: 'CannotOpenTempFile';\n error: Buffer;\n tempFile: string;\n}\n\nexport type ErrorContext =\n | CommonErrorContext\n | OneSourceSourceMapErrorContext\n | UnmappedBytesErrorContext\n | InvalidMappingLineErrorContext\n | InvalidMappingColumnErrorContext\n | CannotOpenTempFileErrorContext;\n\nexport function getErrorMessage(context: ErrorContext): string {\n switch (context.code) {\n case 'NoBundles':\n return 'No file(s) provided';\n\n case 'NoSourceMap':\n return `Unable to find a source map.\nSee ${SOURCE_MAP_INFO_URL}`;\n\n case 'OneSourceSourceMap': {\n return `Your source map only contains one source (${context.filename}).\nThis can happen if you use browserify+uglifyjs, for example, and don't set the --in-source-map flag to uglify.\nSee ${SOURCE_MAP_INFO_URL}`;\n }\n\n case 'UnmappedBytes': {\n const { unmappedBytes, totalBytes } = context;\n\n const bytesString = formatPercent(unmappedBytes, totalBytes, 2);\n\n return `Unable to map ${unmappedBytes}/${totalBytes} bytes (${bytesString}%)`;\n }\n\n case 'InvalidMappingLine': {\n const { generatedLine, maxLine } = context;\n\n return `Your source map refers to generated line ${generatedLine}, but the source only contains ${maxLine} line(s).\nCheck that you are using the correct source map.`;\n }\n\n case 'InvalidMappingColumn': {\n const { generatedLine, generatedColumn, maxColumn } = context;\n\n // Add 1 since generatedColumn is 0-based\n return `Your source map refers to generated column ${\n generatedColumn + 1\n } on line ${generatedLine}, but the source only contains ${maxColumn} column(s) on that line.\nCheck that you are using the correct source map.`;\n }\n\n case 'CannotSaveFile':\n return 'Unable to save HTML to file';\n\n case 'CannotCreateTempFile':\n return 'Unable to create a temporary HTML file';\n\n case 'CannotOpenTempFile': {\n const { error, tempFile } = context;\n\n return `Unable to open web browser. ${error.toString().trim()}\nEither run with --html, --json, --tsv, --file, or view HTML for the visualization at:\n${tempFile}`;\n }\n\n case 'CannotOpenCoverageFile': {\n return 'Unable to open/parse coverage file';\n }\n\n case 'NoCoverageMatches': {\n return 'No matched bundles found for coverages';\n }\n\n default:\n return 'Unknown error';\n }\n}\n"]}
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import { Bundle, ColumnsRange, MappingRange, FileDataMap } from './
|
|
2
|
-
/**
|
|
3
|
-
* Match coverages' ranges to bundles by comparing coverage URL and bundle filename
|
|
4
|
-
*/
|
|
1
|
+
import type { Bundle, ColumnsRange, MappingRange, FileDataMap } from './types';
|
|
5
2
|
export declare function addCoverageRanges(bundles: Bundle[], coverageFilename?: string): Bundle[];
|
|
6
|
-
/**
|
|
7
|
-
* Set covered size for files
|
|
8
|
-
*/
|
|
9
3
|
export declare function setCoveredSizes(line: string, files: FileDataMap, mappingRanges: MappingRange[], coveredRanges: ColumnsRange[]): FileDataMap;
|
|
10
|
-
/**
|
|
11
|
-
* Get heat map color by coverage percent
|
|
12
|
-
*/
|
|
13
4
|
export declare function getColorByPercent(percent: number): string;
|
package/{dist → lib}/coverage.js
RENAMED
|
@@ -1,116 +1,71 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
exports.setCoveredSizes = setCoveredSizes;
|
|
8
|
-
exports.getColorByPercent = getColorByPercent;
|
|
9
|
-
|
|
10
|
-
var _helpers = require('./helpers');
|
|
11
|
-
|
|
12
|
-
var _appError = require('./app-error');
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Convert one-line coverage ranges (exclusive) into per line ranges (inclusive)
|
|
16
|
-
*/
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.getColorByPercent = exports.setCoveredSizes = exports.addCoverageRanges = void 0;
|
|
4
|
+
const url_1 = require('url');
|
|
5
|
+
const helpers_1 = require('./helpers');
|
|
6
|
+
const app_error_1 = require('./app-error');
|
|
17
7
|
function convertRangesToLinesRanges(coverage) {
|
|
18
8
|
const { ranges, text } = coverage;
|
|
19
|
-
const eol =
|
|
9
|
+
const eol = helpers_1.detectEOL(text);
|
|
20
10
|
const eolLength = eol.length;
|
|
21
|
-
const lines = text.split(eol);
|
|
22
|
-
|
|
11
|
+
const lines = text.split(eol);
|
|
23
12
|
let offset = 0;
|
|
24
|
-
const lineRanges = lines.map(line => {
|
|
13
|
+
const lineRanges = lines.map((line) => {
|
|
25
14
|
const lineLength = line.length;
|
|
26
|
-
|
|
27
15
|
if (lineLength === 0) {
|
|
28
16
|
return [];
|
|
29
|
-
}
|
|
30
|
-
|
|
17
|
+
}
|
|
31
18
|
const lineStart = offset;
|
|
32
19
|
const lineEnd = offset + lineLength;
|
|
33
20
|
const lineRanges = ranges.reduce((result, { start, end }) => {
|
|
34
|
-
// Inclusive range start/end within the line
|
|
35
21
|
const startIndex = start - lineStart;
|
|
36
22
|
const endIndex = end - lineStart - 1;
|
|
37
23
|
const lineEndIndex = lineLength - 1;
|
|
38
|
-
|
|
39
24
|
if (start <= lineStart && lineEnd <= end) {
|
|
40
|
-
|
|
41
|
-
result.push({
|
|
42
|
-
start: 0,
|
|
43
|
-
end: lineEndIndex,
|
|
44
|
-
});
|
|
25
|
+
result.push({ start: 0, end: lineEndIndex });
|
|
45
26
|
} else if (lineStart <= start && end <= lineEnd) {
|
|
46
|
-
|
|
47
|
-
result.push({
|
|
48
|
-
start: startIndex,
|
|
49
|
-
end: endIndex,
|
|
50
|
-
});
|
|
27
|
+
result.push({ start: startIndex, end: endIndex });
|
|
51
28
|
} else if (lineStart <= start && start <= lineEnd) {
|
|
52
|
-
|
|
53
|
-
result.push({
|
|
54
|
-
start: startIndex,
|
|
55
|
-
end: lineEndIndex,
|
|
56
|
-
});
|
|
29
|
+
result.push({ start: startIndex, end: lineEndIndex });
|
|
57
30
|
} else if (lineStart <= end && end <= lineEnd) {
|
|
58
|
-
|
|
59
|
-
result.push({
|
|
60
|
-
start: 0,
|
|
61
|
-
end: endIndex,
|
|
62
|
-
});
|
|
31
|
+
result.push({ start: 0, end: endIndex });
|
|
63
32
|
}
|
|
64
|
-
|
|
65
33
|
return result;
|
|
66
|
-
}, []);
|
|
67
|
-
|
|
34
|
+
}, []);
|
|
68
35
|
offset = lineEnd + eolLength;
|
|
69
36
|
return lineRanges;
|
|
70
37
|
});
|
|
71
38
|
return lineRanges;
|
|
72
39
|
}
|
|
73
|
-
|
|
74
40
|
const PATH_SEPARATOR_REGEX = /[/\\]/;
|
|
75
|
-
|
|
76
41
|
function getPathParts(path) {
|
|
77
42
|
return path.split(PATH_SEPARATOR_REGEX).filter(Boolean);
|
|
78
43
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Match coverages' ranges to bundles by comparing coverage URL and bundle filename
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
44
|
function addCoverageRanges(bundles, coverageFilename) {
|
|
84
45
|
if (!coverageFilename) {
|
|
85
46
|
return bundles;
|
|
86
47
|
}
|
|
87
|
-
|
|
88
48
|
try {
|
|
89
|
-
const coverages = JSON.parse(
|
|
49
|
+
const coverages = JSON.parse(helpers_1.getFileContent(coverageFilename));
|
|
90
50
|
const bundlesPaths = bundles.reduce((result, { code }, index) => {
|
|
91
51
|
if (!Buffer.isBuffer(code)) {
|
|
92
52
|
result.push([getPathParts(code).reverse(), index]);
|
|
93
53
|
}
|
|
94
|
-
|
|
95
54
|
return result;
|
|
96
55
|
}, []);
|
|
97
|
-
coverages
|
|
98
|
-
.map(({ url }) => getPathParts(new URL(url).pathname).reverse())
|
|
56
|
+
coverages
|
|
57
|
+
.map(({ url }) => getPathParts(new url_1.URL(url).pathname).reverse())
|
|
99
58
|
.forEach((partsA, coverageIndex) => {
|
|
100
|
-
// Exclude coverages for inlined code (URL doesn't contain a filename)
|
|
101
59
|
if (!partsA.length) return;
|
|
102
|
-
let matchingBundles = [...bundlesPaths];
|
|
103
|
-
|
|
60
|
+
let matchingBundles = [...bundlesPaths];
|
|
104
61
|
for (let i = 0; i < partsA.length; i++) {
|
|
105
62
|
matchingBundles = matchingBundles.filter(
|
|
106
63
|
([partsB]) => i < partsB.length && partsB[i] === partsA[i]
|
|
107
|
-
);
|
|
108
|
-
|
|
64
|
+
);
|
|
109
65
|
if (matchingBundles.length <= 1) {
|
|
110
66
|
break;
|
|
111
67
|
}
|
|
112
68
|
}
|
|
113
|
-
|
|
114
69
|
if (matchingBundles.length === 1) {
|
|
115
70
|
const [[, bundleIndex]] = matchingBundles;
|
|
116
71
|
bundles[bundleIndex].coverageRanges = convertRangesToLinesRanges(
|
|
@@ -119,38 +74,22 @@ function addCoverageRanges(bundles, coverageFilename) {
|
|
|
119
74
|
}
|
|
120
75
|
});
|
|
121
76
|
} catch (error) {
|
|
122
|
-
throw new
|
|
123
|
-
{
|
|
124
|
-
code: 'CannotOpenCoverageFile',
|
|
125
|
-
},
|
|
126
|
-
error
|
|
127
|
-
);
|
|
77
|
+
throw new app_error_1.AppError({ code: 'CannotOpenCoverageFile' }, error);
|
|
128
78
|
}
|
|
129
|
-
|
|
130
79
|
if (bundles.every(({ coverageRanges }) => coverageRanges === undefined)) {
|
|
131
|
-
throw new
|
|
132
|
-
code: 'NoCoverageMatches',
|
|
133
|
-
});
|
|
80
|
+
throw new app_error_1.AppError({ code: 'NoCoverageMatches' });
|
|
134
81
|
}
|
|
135
|
-
|
|
136
82
|
return bundles;
|
|
137
83
|
}
|
|
138
|
-
|
|
139
|
-
* Find overlaps in arrays of column ranges, using ratcheting pointers instead of nested loops for
|
|
140
|
-
* O(n) runtime instead of O(n^2)
|
|
141
|
-
*/
|
|
142
|
-
|
|
84
|
+
exports.addCoverageRanges = addCoverageRanges;
|
|
143
85
|
function findCoveredMappingRanges(mappingRanges, coveredRanges) {
|
|
144
86
|
let i = 0;
|
|
145
87
|
let j = 0;
|
|
146
88
|
const result = [];
|
|
147
|
-
|
|
148
89
|
while (i < mappingRanges.length && j < coveredRanges.length) {
|
|
149
90
|
const mappingRange = mappingRanges[i];
|
|
150
91
|
const coveredRange = coveredRanges[j];
|
|
151
|
-
|
|
152
92
|
if (mappingRange.start <= coveredRange.end && coveredRange.start <= mappingRange.end) {
|
|
153
|
-
// Overlaps, calculate amount, move to next coverage range
|
|
154
93
|
const end = Math.min(coveredRange.end, mappingRange.end);
|
|
155
94
|
const start = Math.max(mappingRange.start, coveredRange.start);
|
|
156
95
|
result.push({
|
|
@@ -158,35 +97,24 @@ function findCoveredMappingRanges(mappingRanges, coveredRanges) {
|
|
|
158
97
|
end,
|
|
159
98
|
source: mappingRange.source,
|
|
160
99
|
});
|
|
161
|
-
|
|
162
100
|
if (
|
|
163
101
|
mappingRanges[i + 1] !== undefined &&
|
|
164
102
|
mappingRanges[i + 1].start <= coveredRange.end &&
|
|
165
103
|
mappingRanges[i + 1].end >= coveredRange.start
|
|
166
104
|
) {
|
|
167
|
-
// Next module also overlaps current coverage range, advance to next module instead of advancing coverage
|
|
168
105
|
i++;
|
|
169
106
|
} else {
|
|
170
|
-
// Check next coverage range, it may also overlap this module range
|
|
171
107
|
j++;
|
|
172
108
|
}
|
|
173
109
|
} else if (mappingRange.end < coveredRange.start) {
|
|
174
|
-
// Module comes entirely before coverageRange, check next module range
|
|
175
110
|
i++;
|
|
176
111
|
}
|
|
177
|
-
|
|
178
112
|
if (coveredRange.end < mappingRange.start) {
|
|
179
|
-
// Module range comes entirely after coverage range, check next coverage range
|
|
180
113
|
j++;
|
|
181
114
|
}
|
|
182
115
|
}
|
|
183
|
-
|
|
184
116
|
return result;
|
|
185
117
|
}
|
|
186
|
-
/**
|
|
187
|
-
* Set covered size for files
|
|
188
|
-
*/
|
|
189
|
-
|
|
190
118
|
function setCoveredSizes(line, files, mappingRanges, coveredRanges) {
|
|
191
119
|
findCoveredMappingRanges(mappingRanges, coveredRanges).forEach(({ start, end, source }) => {
|
|
192
120
|
const rangeByteLength = Buffer.byteLength(line.substring(start, end + 1));
|
|
@@ -196,46 +124,19 @@ function setCoveredSizes(line, files, mappingRanges, coveredRanges) {
|
|
|
196
124
|
});
|
|
197
125
|
return files;
|
|
198
126
|
}
|
|
199
|
-
|
|
127
|
+
exports.setCoveredSizes = setCoveredSizes;
|
|
200
128
|
const percentColors = [
|
|
201
|
-
{
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
r: 0xff,
|
|
205
|
-
g: 0x00,
|
|
206
|
-
b: 0,
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
percent: 0.5,
|
|
211
|
-
color: {
|
|
212
|
-
r: 0xff,
|
|
213
|
-
g: 0xff,
|
|
214
|
-
b: 0,
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
percent: 1.0,
|
|
219
|
-
color: {
|
|
220
|
-
r: 0x00,
|
|
221
|
-
g: 0xff,
|
|
222
|
-
b: 0,
|
|
223
|
-
},
|
|
224
|
-
},
|
|
129
|
+
{ percent: 0.0, color: { r: 0xff, g: 0x00, b: 0 } },
|
|
130
|
+
{ percent: 0.5, color: { r: 0xff, g: 0xff, b: 0 } },
|
|
131
|
+
{ percent: 1.0, color: { r: 0x00, g: 0xff, b: 0 } },
|
|
225
132
|
];
|
|
226
|
-
/**
|
|
227
|
-
* Get heat map color by coverage percent
|
|
228
|
-
*/
|
|
229
|
-
|
|
230
133
|
function getColorByPercent(percent) {
|
|
231
134
|
let i = 1;
|
|
232
|
-
|
|
233
135
|
for (; i < percentColors.length - 1; i++) {
|
|
234
136
|
if (percent < percentColors[i].percent) {
|
|
235
137
|
break;
|
|
236
138
|
}
|
|
237
139
|
}
|
|
238
|
-
|
|
239
140
|
const lowerColor = percentColors[i - 1];
|
|
240
141
|
const upperColor = percentColors[i];
|
|
241
142
|
const rangeWithinColors = upperColor.percent - lowerColor.percent;
|
|
@@ -247,3 +148,5 @@ function getColorByPercent(percent) {
|
|
|
247
148
|
const b = Math.floor(lowerColor.color.b * percentLower + upperColor.color.b * percentUpper);
|
|
248
149
|
return `rgb(${r}, ${g}, ${b})`;
|
|
249
150
|
}
|
|
151
|
+
exports.getColorByPercent = getColorByPercent;
|
|
152
|
+
//# sourceMappingURL=coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../src/lib/coverage.ts"],"names":[],"mappings":"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","sourcesContent":["import { URL } from 'url';\n\nimport { getFileContent, detectEOL } from './helpers';\nimport { AppError } from './app-error';\n\nimport type { Bundle, Coverage, ColumnsRange, MappingRange, FileDataMap } from './types';\n\n/**\n * Convert one-line coverage ranges (exclusive) into per line ranges (inclusive)\n */\nfunction convertRangesToLinesRanges(coverage: Coverage): ColumnsRange[][] {\n const { ranges, text } = coverage;\n\n const eol = detectEOL(text);\n const eolLength = eol.length;\n const lines = text.split(eol);\n\n // Current line offset\n let offset = 0;\n\n const lineRanges = lines.map((line) => {\n const lineLength = line.length;\n\n if (lineLength === 0) {\n return [];\n }\n\n // Exclusive line start/end\n const lineStart = offset;\n const lineEnd = offset + lineLength;\n\n const lineRanges = ranges.reduce<ColumnsRange[]>((result, { start, end }) => {\n // Inclusive range start/end within the line\n const startIndex = start - lineStart;\n const endIndex = end - lineStart - 1;\n const lineEndIndex = lineLength - 1;\n\n if (start <= lineStart && lineEnd <= end) {\n // Range includes line range\n result.push({ start: 0, end: lineEndIndex });\n } else if (lineStart <= start && end <= lineEnd) {\n // Line range includes range\n result.push({ start: startIndex, end: endIndex });\n } else if (lineStart <= start && start <= lineEnd) {\n // Range starts within line range\n result.push({ start: startIndex, end: lineEndIndex });\n } else if (lineStart <= end && end <= lineEnd) {\n // Range ends within line range\n result.push({ start: 0, end: endIndex });\n }\n\n return result;\n }, []);\n\n // Move to next line jumping over EOL character\n offset = lineEnd + eolLength;\n\n return lineRanges;\n });\n\n return lineRanges;\n}\n\nconst PATH_SEPARATOR_REGEX = /[/\\\\]/;\n\nfunction getPathParts(path: string): string[] {\n return path.split(PATH_SEPARATOR_REGEX).filter(Boolean);\n}\n\n/**\n * Match coverages' ranges to bundles by comparing coverage URL and bundle filename\n */\nexport function addCoverageRanges(bundles: Bundle[], coverageFilename?: string): Bundle[] {\n if (!coverageFilename) {\n return bundles;\n }\n\n try {\n const coverages: Coverage[] = JSON.parse(getFileContent(coverageFilename));\n\n const bundlesPaths = bundles.reduce<[string[], number][]>((result, { code }, index) => {\n if (!Buffer.isBuffer(code)) {\n result.push([getPathParts(code).reverse(), index]);\n }\n\n return result;\n }, []);\n\n coverages\n // pathname contains filename\n .map(({ url }) => getPathParts(new URL(url).pathname).reverse())\n .forEach((partsA, coverageIndex) => {\n // Exclude coverages for inlined code (URL doesn't contain a filename)\n if (!partsA.length) return;\n let matchingBundles = [...bundlesPaths];\n\n // Start from filename and go up to path root\n for (let i = 0; i < partsA.length; i++) {\n matchingBundles = matchingBundles.filter(\n ([partsB]) => i < partsB.length && partsB[i] === partsA[i]\n );\n\n // Stop when exact (among bundles) match found or no matches found\n if (matchingBundles.length <= 1) {\n break;\n }\n }\n\n if (matchingBundles.length === 1) {\n const [[, bundleIndex]] = matchingBundles;\n\n bundles[bundleIndex].coverageRanges = convertRangesToLinesRanges(\n coverages[coverageIndex]\n );\n }\n });\n } catch (error) {\n throw new AppError({ code: 'CannotOpenCoverageFile' }, error);\n }\n\n if (bundles.every(({ coverageRanges }) => coverageRanges === undefined)) {\n throw new AppError({ code: 'NoCoverageMatches' });\n }\n\n return bundles;\n}\n\n/**\n * Find overlaps in arrays of column ranges, using ratcheting pointers instead of nested loops for\n * O(n) runtime instead of O(n^2)\n */\nfunction findCoveredMappingRanges(\n mappingRanges: MappingRange[],\n coveredRanges: ColumnsRange[]\n): MappingRange[] {\n let i = 0;\n let j = 0;\n\n const result: MappingRange[] = [];\n\n while (i < mappingRanges.length && j < coveredRanges.length) {\n const mappingRange = mappingRanges[i];\n const coveredRange = coveredRanges[j];\n\n if (mappingRange.start <= coveredRange.end && coveredRange.start <= mappingRange.end) {\n // Overlaps, calculate amount, move to next coverage range\n const end = Math.min(coveredRange.end, mappingRange.end);\n const start = Math.max(mappingRange.start, coveredRange.start);\n\n result.push({\n start,\n end,\n source: mappingRange.source,\n });\n\n if (\n mappingRanges[i + 1] !== undefined &&\n mappingRanges[i + 1].start <= coveredRange.end &&\n mappingRanges[i + 1].end >= coveredRange.start\n ) {\n // Next module also overlaps current coverage range, advance to next module instead of advancing coverage\n i++;\n } else {\n // Check next coverage range, it may also overlap this module range\n j++;\n }\n } else if (mappingRange.end < coveredRange.start) {\n // Module comes entirely before coverageRange, check next module range\n i++;\n }\n if (coveredRange.end < mappingRange.start) {\n // Module range comes entirely after coverage range, check next coverage range\n j++;\n }\n }\n\n return result;\n}\n\n/**\n * Set covered size for files\n */\nexport function setCoveredSizes(\n line: string,\n files: FileDataMap,\n mappingRanges: MappingRange[],\n coveredRanges: ColumnsRange[]\n): FileDataMap {\n findCoveredMappingRanges(mappingRanges, coveredRanges).forEach(({ start, end, source }) => {\n const rangeByteLength = Buffer.byteLength(line.substring(start, end + 1));\n\n let coveredSize = files[source].coveredSize || 0;\n\n coveredSize += rangeByteLength;\n\n files[source].coveredSize = coveredSize;\n });\n\n return files;\n}\n\nconst percentColors = [\n { percent: 0.0, color: { r: 0xff, g: 0x00, b: 0 } },\n { percent: 0.5, color: { r: 0xff, g: 0xff, b: 0 } },\n { percent: 1.0, color: { r: 0x00, g: 0xff, b: 0 } },\n];\n\n/**\n * Get heat map color by coverage percent\n */\nexport function getColorByPercent(percent: number): string {\n let i = 1;\n\n for (; i < percentColors.length - 1; i++) {\n if (percent < percentColors[i].percent) {\n break;\n }\n }\n\n const lowerColor = percentColors[i - 1];\n const upperColor = percentColors[i];\n const rangeWithinColors = upperColor.percent - lowerColor.percent;\n const rangePercent = (percent - lowerColor.percent) / rangeWithinColors;\n const percentLower = 1 - rangePercent;\n const percentUpper = rangePercent;\n\n const r = Math.floor(lowerColor.color.r * percentLower + upperColor.color.r * percentUpper);\n const g = Math.floor(lowerColor.color.g * percentLower + upperColor.color.g * percentUpper);\n const b = Math.floor(lowerColor.color.b * percentLower + upperColor.color.b * percentUpper);\n\n return `rgb(${r}, ${g}, ${b})`;\n}\n"]}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import { Bundle,
|
|
1
|
+
import type { Bundle, ExploreBundleResult, ExploreOptions, FileDataMap } from './types';
|
|
2
2
|
export declare const UNMAPPED_KEY = "[unmapped]";
|
|
3
3
|
export declare const SOURCE_MAP_COMMENT_KEY = "[sourceMappingURL]";
|
|
4
4
|
export declare const NO_SOURCE_KEY = "[no source]";
|
|
5
5
|
export declare const EOL_KEY = "[EOLs]";
|
|
6
6
|
export declare const SPECIAL_FILENAMES: string[];
|
|
7
|
-
/**
|
|
8
|
-
* Analyze a bundle
|
|
9
|
-
*/
|
|
10
7
|
export declare function exploreBundle(bundle: Bundle, options: ExploreOptions): Promise<ExploreBundleResult>;
|
|
11
8
|
export declare function adjustSourcePaths(fileSizeMap: FileDataMap, options: ExploreOptions): FileDataMap;
|
package/lib/explore.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
var __importDefault =
|
|
3
|
+
(this && this.__importDefault) ||
|
|
4
|
+
function (mod) {
|
|
5
|
+
return mod && mod.__esModule ? mod : { default: mod };
|
|
6
|
+
};
|
|
7
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
8
|
+
exports.adjustSourcePaths = exports.exploreBundle = exports.SPECIAL_FILENAMES = exports.EOL_KEY = exports.NO_SOURCE_KEY = exports.SOURCE_MAP_COMMENT_KEY = exports.UNMAPPED_KEY = void 0;
|
|
9
|
+
const convert_source_map_1 = __importDefault(require('convert-source-map'));
|
|
10
|
+
const path_1 = __importDefault(require('path'));
|
|
11
|
+
const source_map_1 = require('source-map');
|
|
12
|
+
const gzip_size_1 = __importDefault(require('gzip-size'));
|
|
13
|
+
const lodash_1 = require('lodash');
|
|
14
|
+
const api_1 = require('./api');
|
|
15
|
+
const helpers_1 = require('./helpers');
|
|
16
|
+
const app_error_1 = require('./app-error');
|
|
17
|
+
const coverage_1 = require('./coverage');
|
|
18
|
+
exports.UNMAPPED_KEY = '[unmapped]';
|
|
19
|
+
exports.SOURCE_MAP_COMMENT_KEY = '[sourceMappingURL]';
|
|
20
|
+
exports.NO_SOURCE_KEY = '[no source]';
|
|
21
|
+
exports.EOL_KEY = '[EOLs]';
|
|
22
|
+
exports.SPECIAL_FILENAMES = [
|
|
23
|
+
exports.UNMAPPED_KEY,
|
|
24
|
+
exports.SOURCE_MAP_COMMENT_KEY,
|
|
25
|
+
exports.NO_SOURCE_KEY,
|
|
26
|
+
exports.EOL_KEY,
|
|
27
|
+
];
|
|
28
|
+
async function exploreBundle(bundle, options) {
|
|
29
|
+
const { code, map, coverageRanges } = bundle;
|
|
30
|
+
const sourceMapData = await loadSourceMap(code, map);
|
|
31
|
+
const sizes = computeFileSizes(sourceMapData, options, coverageRanges);
|
|
32
|
+
const files = adjustSourcePaths(sizes.files, options);
|
|
33
|
+
sourceMapData.consumer.destroy();
|
|
34
|
+
return {
|
|
35
|
+
bundleName: api_1.getBundleName(bundle),
|
|
36
|
+
...sizes,
|
|
37
|
+
files,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
exports.exploreBundle = exploreBundle;
|
|
41
|
+
async function loadSourceMap(codeFile, sourceMapFile) {
|
|
42
|
+
const codeFileContent = helpers_1.getFileContent(codeFile);
|
|
43
|
+
let consumer;
|
|
44
|
+
if (sourceMapFile) {
|
|
45
|
+
const sourceMapFileContent = helpers_1.getFileContent(sourceMapFile);
|
|
46
|
+
consumer = await new source_map_1.SourceMapConsumer(sourceMapFileContent);
|
|
47
|
+
} else {
|
|
48
|
+
let converter = convert_source_map_1.default.fromSource(codeFileContent);
|
|
49
|
+
if (!converter && !Buffer.isBuffer(codeFile)) {
|
|
50
|
+
converter = convert_source_map_1.default.fromMapFileSource(
|
|
51
|
+
codeFileContent,
|
|
52
|
+
path_1.default.dirname(codeFile)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (!converter) {
|
|
56
|
+
throw new app_error_1.AppError({ code: 'NoSourceMap' });
|
|
57
|
+
}
|
|
58
|
+
consumer = await new source_map_1.SourceMapConsumer(converter.toJSON());
|
|
59
|
+
}
|
|
60
|
+
if (!consumer) {
|
|
61
|
+
throw new app_error_1.AppError({ code: 'NoSourceMap' });
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
consumer,
|
|
65
|
+
codeFileContent,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const COMMENT_REGEX = convert_source_map_1.default.commentRegex;
|
|
69
|
+
const MAP_FILE_COMMENT_REGEX = convert_source_map_1.default.mapFileCommentRegex;
|
|
70
|
+
function getSourceMapComment(fileContent) {
|
|
71
|
+
const sourceMapComment =
|
|
72
|
+
helpers_1.getFirstRegexMatch(COMMENT_REGEX, fileContent) ||
|
|
73
|
+
helpers_1.getFirstRegexMatch(MAP_FILE_COMMENT_REGEX, fileContent) ||
|
|
74
|
+
'';
|
|
75
|
+
return sourceMapComment.trim();
|
|
76
|
+
}
|
|
77
|
+
function isReferencingEOL(context, maxColumnIndex) {
|
|
78
|
+
const { generatedLine, generatedColumn, source, consumer } = context;
|
|
79
|
+
if (maxColumnIndex - generatedColumn > 2) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
if (!source) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (context.mapReferenceEOLSources.has(source)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
const content = consumer.sourceContentFor(source, true);
|
|
89
|
+
if (!content) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const { line, column } = consumer.originalPositionFor({
|
|
93
|
+
line: generatedLine,
|
|
94
|
+
column: generatedColumn,
|
|
95
|
+
});
|
|
96
|
+
if (line === null || column === null) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (helpers_1.isEOLAtPosition(content, [line, column])) {
|
|
100
|
+
context.mapReferenceEOLSources.add(source);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
function checkInvalidMappingColumn(context) {
|
|
106
|
+
const { line, generatedLine, generatedColumn } = context;
|
|
107
|
+
const maxColumnIndex = line.length - 1;
|
|
108
|
+
if (generatedColumn > maxColumnIndex) {
|
|
109
|
+
if (isReferencingEOL(context, maxColumnIndex)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
throw new app_error_1.AppError({
|
|
113
|
+
code: 'InvalidMappingColumn',
|
|
114
|
+
generatedLine,
|
|
115
|
+
generatedColumn,
|
|
116
|
+
maxColumn: line.length,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function computeFileSizes(sourceMapData, options, coverageRanges) {
|
|
121
|
+
const { consumer, codeFileContent: fileContent } = sourceMapData;
|
|
122
|
+
const sourceMapComment = getSourceMapComment(fileContent);
|
|
123
|
+
const sourceContent = fileContent.replace(sourceMapComment, '').trim();
|
|
124
|
+
const eol = helpers_1.detectEOL(fileContent);
|
|
125
|
+
const lines = sourceContent.split(eol);
|
|
126
|
+
const mappingRanges = [];
|
|
127
|
+
const context = {
|
|
128
|
+
generatedLine: -1,
|
|
129
|
+
generatedColumn: -1,
|
|
130
|
+
line: '',
|
|
131
|
+
source: null,
|
|
132
|
+
consumer,
|
|
133
|
+
mapReferenceEOLSources: new Set(),
|
|
134
|
+
};
|
|
135
|
+
consumer.computeColumnSpans();
|
|
136
|
+
consumer.eachMapping(({ source, generatedLine, generatedColumn, lastGeneratedColumn }) => {
|
|
137
|
+
const lineIndex = generatedLine - 1;
|
|
138
|
+
const line = lines[lineIndex];
|
|
139
|
+
if (line === undefined) {
|
|
140
|
+
if (options.noBorderChecks) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
throw new app_error_1.AppError({
|
|
144
|
+
code: 'InvalidMappingLine',
|
|
145
|
+
generatedLine,
|
|
146
|
+
maxLine: lines.length,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
context.generatedLine = generatedLine;
|
|
150
|
+
context.generatedColumn = lastGeneratedColumn || generatedColumn;
|
|
151
|
+
context.line = line;
|
|
152
|
+
context.source = source;
|
|
153
|
+
if (!options.noBorderChecks) {
|
|
154
|
+
checkInvalidMappingColumn(context);
|
|
155
|
+
}
|
|
156
|
+
const start = generatedColumn;
|
|
157
|
+
const end = lastGeneratedColumn === null ? line.length - 1 : lastGeneratedColumn;
|
|
158
|
+
const lineRanges = mappingRanges[lineIndex] || [];
|
|
159
|
+
lineRanges.push({
|
|
160
|
+
start,
|
|
161
|
+
end,
|
|
162
|
+
source: source === null ? exports.NO_SOURCE_KEY : source,
|
|
163
|
+
});
|
|
164
|
+
mappingRanges[lineIndex] = lineRanges;
|
|
165
|
+
});
|
|
166
|
+
let files = {};
|
|
167
|
+
let mappedBytes = 0;
|
|
168
|
+
const getSize = options.gzip ? gzip_size_1.default.sync : Buffer.byteLength;
|
|
169
|
+
mappingRanges.forEach((lineRanges, lineIndex) => {
|
|
170
|
+
const line = lines[lineIndex];
|
|
171
|
+
const mergedRanges = helpers_1.mergeRanges(lineRanges);
|
|
172
|
+
mergedRanges.forEach(({ start, end, source }) => {
|
|
173
|
+
const rangeString = line.substring(start, end + 1);
|
|
174
|
+
const rangeByteLength = getSize(rangeString);
|
|
175
|
+
if (!files[source]) {
|
|
176
|
+
files[source] = { size: 0 };
|
|
177
|
+
}
|
|
178
|
+
files[source].size += rangeByteLength;
|
|
179
|
+
mappedBytes += rangeByteLength;
|
|
180
|
+
});
|
|
181
|
+
if (coverageRanges) {
|
|
182
|
+
files = coverage_1.setCoveredSizes(line, files, mergedRanges, coverageRanges[lineIndex]);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
const sourceMapCommentBytes = getSize(sourceMapComment);
|
|
186
|
+
const eolBytes = helpers_1.getOccurrencesCount(eol, fileContent) * Buffer.byteLength(eol);
|
|
187
|
+
const totalBytes = getSize(fileContent);
|
|
188
|
+
let unmappedBytes;
|
|
189
|
+
if (!options.excludeSourceMapComment) {
|
|
190
|
+
files[exports.SOURCE_MAP_COMMENT_KEY] = { size: sourceMapCommentBytes };
|
|
191
|
+
}
|
|
192
|
+
if (!options.onlyMapped) {
|
|
193
|
+
unmappedBytes = totalBytes - mappedBytes - sourceMapCommentBytes - eolBytes;
|
|
194
|
+
files[exports.UNMAPPED_KEY] = { size: unmappedBytes };
|
|
195
|
+
}
|
|
196
|
+
if (eolBytes > 0) {
|
|
197
|
+
files[exports.EOL_KEY] = { size: eolBytes };
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
...(options.excludeSourceMapComment
|
|
201
|
+
? { totalBytes: totalBytes - sourceMapCommentBytes }
|
|
202
|
+
: { totalBytes }),
|
|
203
|
+
mappedBytes,
|
|
204
|
+
...(!options.onlyMapped && { unmappedBytes }),
|
|
205
|
+
eolBytes,
|
|
206
|
+
sourceMapCommentBytes,
|
|
207
|
+
files,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function adjustSourcePaths(fileSizeMap, options) {
|
|
211
|
+
if (!options.noRoot) {
|
|
212
|
+
const prefix = helpers_1.getCommonPathPrefix(Object.keys(fileSizeMap));
|
|
213
|
+
const length = prefix.length;
|
|
214
|
+
if (length) {
|
|
215
|
+
fileSizeMap = lodash_1.mapKeys(fileSizeMap, (size, source) => source.slice(length));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (options.replaceMap) {
|
|
219
|
+
fileSizeMap = Object.entries(options.replaceMap).reduce((result, [before, after]) => {
|
|
220
|
+
const regexp = new RegExp(before, 'g');
|
|
221
|
+
return lodash_1.mapKeys(result, (size, source) => source.replace(regexp, after));
|
|
222
|
+
}, fileSizeMap);
|
|
223
|
+
}
|
|
224
|
+
return fileSizeMap;
|
|
225
|
+
}
|
|
226
|
+
exports.adjustSourcePaths = adjustSourcePaths;
|
|
227
|
+
//# sourceMappingURL=explore.js.map
|