1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | export default function sourceRange(
|
14 | source,
|
15 | startOffset,
|
16 | endOffset,
|
17 | ignoreNextLineComment = ' coverage ignore next line'
|
18 | ) {
|
19 | if (typeof source !== 'string')
|
20 | throw new TypeError('First argument `source` must be a string.');
|
21 |
|
22 | if (typeof startOffset !== 'number')
|
23 | throw new TypeError('Second argument `startOffset` must be a number.');
|
24 |
|
25 | if (typeof endOffset !== 'number')
|
26 | throw new TypeError('Third argument `endOffset` must be a number.');
|
27 |
|
28 | if (
|
29 | typeof ignoreNextLineComment !== 'string' &&
|
30 | ignoreNextLineComment !== false
|
31 | )
|
32 | throw new TypeError(
|
33 | 'Fourth argument `ignoreNextLineComment` must be a string or `false`.'
|
34 | );
|
35 |
|
36 | if (ignoreNextLineComment)
|
37 | var ignoreNextLineCommentLowerCase = `//${ignoreNextLineComment.toLowerCase()}`;
|
38 |
|
39 | const range = {
|
40 | start: { offset: startOffset },
|
41 | end: { offset: endOffset },
|
42 | };
|
43 | const lines = source.split(/^/gmu);
|
44 |
|
45 | let lineOffset = 0;
|
46 |
|
47 | for (const [lineIndex, lineSource] of lines.entries()) {
|
48 | const nextLineOffset = lineOffset + lineSource.length;
|
49 |
|
50 | if (
|
51 | !('line' in range.start) &&
|
52 | startOffset >= lineOffset &&
|
53 | startOffset < nextLineOffset
|
54 | ) {
|
55 | range.start.line = lineIndex + 1;
|
56 | range.start.column = startOffset - lineOffset + 1;
|
57 | range.ignore =
|
58 |
|
59 | !!ignoreNextLineComment &&
|
60 |
|
61 | !!lineIndex &&
|
62 |
|
63 |
|
64 | lines[lineIndex - 1]
|
65 | .trim()
|
66 | .toLowerCase()
|
67 | .endsWith(ignoreNextLineCommentLowerCase);
|
68 | }
|
69 |
|
70 | if (endOffset >= lineOffset && endOffset < nextLineOffset) {
|
71 | range.end.line = lineIndex + 1;
|
72 | range.end.column = endOffset - lineOffset + 1;
|
73 | break;
|
74 | }
|
75 |
|
76 | lineOffset = nextLineOffset;
|
77 | }
|
78 |
|
79 | return range;
|
80 | }
|