All files script-coverage.ts

100% Statements 78/78
97.82% Branches 45/46
100% Functions 9/9
100% Lines 73/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170            54x           2952x 42x     2910x 2910x 42671x                 2910x 2910x 42671x 5898x       2910x 1906x     1004x         1906x 104088x 104088x   65321x     1906x   1906x   27547x     1906x   1906x   1906x 9684895x 9684895x 9684895x   9684895x 46276x         46276x       1906x       1004x   1004x 15124x 15044x 15044x       58080x   1004x 1004x 1004x 1004x   1004x 29604x   29604x 27852x 27852x   27852x 5888x   21964x       29604x 30088x   30088x 15044x   15044x     30088x     29604x     1004x         27852x   27852x 41752x   41752x         34200x       27852x             9025028x 9025028x   9025028x 98450766x 98450766x   98450766x 8730012x 89720754x 15366616x   74354138x       295016x    
import { type Profiler } from "node:inspector";
 
type Normalized = { start: number; end: number; count: number };
type RawRange = Normalized & { area: number; order: number };
 
/** Files larger than this use event sweep to avoid allocating huge hits arrays */
const HITS_ARRAY_MAX_SIZE = 2_000_000;
 
export function normalize(
  scriptCoverage: Pick<Profiler.ScriptCoverage, "functions">,
  hitsArrayMaxSize = HITS_ARRAY_MAX_SIZE,
): Normalized[] {
  if (scriptCoverage.functions.length === 0) {
    return [];
  }
 
  let order = 0;
  const ranges: RawRange[] = scriptCoverage.functions.flatMap((fn) =>
    fn.ranges.map((range) => ({
      start: range.startOffset,
      end: range.endOffset,
      count: range.count,
      area: range.endOffset - range.startOffset,
      order: order++,
    })),
  );
 
  let maxEnd = 0;
  for (const r of ranges) {
    if (r.end > maxEnd) {
      maxEnd = r.end;
    }
  }
 
  if (maxEnd <= hitsArrayMaxSize) {
    return normalizeWithHitsArray(ranges, maxEnd);
  }
 
  return normalizeWithEventSweep(ranges);
}
 
function normalizeWithHitsArray(ranges: RawRange[], maxEnd: number): Normalized[] {
  // Paint ranges into hits array so that the most specific range is applied last
  ranges.sort((a, b) => {
    const diff = b.area - a.area;
    if (diff !== 0) return diff;
 
    return a.end - b.end;
  });
 
  const hits = new Uint32Array(maxEnd);
 
  for (const range of ranges) {
    // V8's endOffset is exclusive - the offset at range's end belongs to the parent range
    hits.fill(range.count, range.start, range.end);
  }
 
  const normalized: Normalized[] = [];
 
  let start = 0;
 
  for (let end = 1; end <= hits.length; end++) {
    const isLast = end === hits.length;
    const current = isLast ? null : hits[end];
    const previous = hits[start];
 
    if (current !== previous || isLast) {
      normalized.push({
        start,
        end: end - 1,
        count: previous,
      });
      start = end;
    }
  }
 
  return normalized;
}
 
function normalizeWithEventSweep(ranges: RawRange[]): Normalized[] {
  const events: { offset: number; range: RawRange; isStart: boolean }[] = [];
 
  for (const range of ranges) {
    if (range.start < range.end) {
      events.push({ offset: range.start, range, isStart: true });
      events.push({ offset: range.end, range, isStart: false });
    }
  }
 
  events.sort((a, b) => a.offset - b.offset);
 
  const active: RawRange[] = [];
  const normalized: Normalized[] = [];
  let cursor = events.length > 0 ? events[0].offset : 0;
  let index = 0;
 
  while (index < events.length) {
    const offset = events[index].offset;
 
    if (active.length > 0 && cursor < offset) {
      const count = getMostSpecificRange(active).count;
      const previous = normalized.at(-1);
 
      if (previous && previous.end + 1 === cursor && previous.count === count) {
        previous.end = offset - 1;
      } else {
        normalized.push({ start: cursor, end: offset - 1, count });
      }
    }
 
    while (index < events.length && events[index].offset === offset) {
      const event = events[index];
 
      if (event.isStart) {
        active.push(event.range);
      } else {
        active.splice(active.indexOf(event.range), 1);
      }
 
      index++;
    }
 
    cursor = offset;
  }
 
  return normalized;
}
 
/** Pick the range that painting would have applied last in `normalizeWithHitsArray` */
function getMostSpecificRange(ranges: RawRange[]): RawRange {
  let winner = ranges[0];
 
  for (let index = 1; index < ranges.length; index++) {
    const range = ranges[index];
 
    if (
      range.area < winner.area ||
      (range.area === winner.area && range.end > winner.end) ||
      (range.area === winner.area && range.end === winner.end && range.order > winner.order)
    ) {
      winner = range;
    }
  }
 
  return winner;
}
 
export function getCount(
  offset: Pick<Profiler.CoverageRange, "startOffset" | "endOffset">,
  coverages: Normalized[],
) {
  let low = 0;
  let high = coverages.length - 1;
 
  while (low <= high) {
    const mid = Math.floor((low + high) / 2);
    const coverage = coverages[mid];
 
    if (coverage.start <= offset.startOffset && offset.startOffset <= coverage.end) {
      return coverage.count;
    } else if (offset.startOffset < coverage.start) {
      high = mid - 1;
    } else {
      low = mid + 1;
    }
  }
 
  return 0;
}