All files ignore-hints.ts

100% Statements 25/25
94.44% Branches 17/18
100% Functions 1/1
100% Lines 25/25

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                58x     58x   58x             996x 996x   996x 996x   996x 795327x               279x 279x 279x   279x     795327x       7571x 7571x                   7571x 7571x   7571x 24x     7547x 295x 295x       795303x     972x                                                                                                                      
import jsTokens from "js-tokens";
 
export interface IgnoreHint {
  type: "if" | "else" | "next" | "file";
  loc: { start: number; end: number };
}
 
const IGNORE_PATTERN =
  /^\s*(?:istanbul|[cv]8|node:coverage)\s+ignore\s+(if|else|next|file)(?=\W|$)/;
 
const IGNORE_LINES_PATTERN =
  /\s*(?:istanbul|[cv]8|node:coverage)\s+ignore\s+(start|stop)(?=\W|$)/;
 
const EOL_PATTERN = /\r?\n/g;
 
/**
 * Parse ignore hints from **Javascript** code based on AST
 * - Most AST parsers don't emit comments in AST like Acorn does, so parse comments manually instead.
 */
export function getIgnoreHints(code: string): IgnoreHint[] {
  const ignoreHints: IgnoreHint[] = [];
  const tokens = jsTokens(code);
 
  let current = 0;
  let previousTokenWasIgnoreHint = false;
 
  for (const token of tokens) {
    if (
      previousTokenWasIgnoreHint &&
      token.type !== "WhiteSpace" &&
      token.type !== "LineTerminatorSequence"
    ) {
      // Make the comment end reach all the way to the next node so that
      // it's easier to check for ignore hints when inspecting node, kind of like
      // leadingComments AST attribute.
      const previous = ignoreHints.at(-1);
      Eif (previous) {
        previous.loc.end = current;
      }
      previousTokenWasIgnoreHint = false;
    }
 
    if (
      token.type === "SingleLineComment" ||
      token.type === "MultiLineComment"
    ) {
      const loc = { start: current, end: current + token.value.length };
      const comment = token.value
        // Start of multiline comment
        .replace(/^\/\*\*/, "")
        .replace(/^\/\*/, "")
        // End of multiline comment
        .replace(/\*\*\/$/, "")
        .replace(/\*\/$/, "")
        // Inline comment
        .replace(/^\/\//, "");
 
      const groups = comment.match(IGNORE_PATTERN);
      const type = groups?.[1];
 
      if (type === "file") {
        return [{ type: "file", loc: { start: 0, end: 0 } }];
      }
 
      if (type === "if" || type === "else" || type === "next") {
        ignoreHints.push({ type, loc });
        previousTokenWasIgnoreHint = true;
      }
    }
 
    current += token.value.length;
  }
 
  return ignoreHints;
}
 
/**
 * Parse ignore start/stop hints from **text file** based on regular expressions
 * - Does not understand what a comment is in Javascript (or JSX, Vue, Svelte)
 * - Parses source code (JS, TS, Vue, Svelte, anything) based on text search by
 *   matching for `/* <name> ignore start *\/` pattern - not by looking for real comments
 *
 * ```js
 * /* v8 ignore start *\/
 * <!-- /* v8 ignore start *\/ -->
 * <SomeFrameworkComment content="/* v8 ignore start *\/">
 * ```
 */
export function getIgnoredLines(text?: string): Set<number> {
  if (!text) {
    return new Set();
  }
 
  const ranges: { start: number; stop: number }[] = [];
  let lineNumber = 0;
 
  for (const line of text.split(EOL_PATTERN)) {
    lineNumber++;
 
    const match = line.match(IGNORE_LINES_PATTERN);
    if (match) {
      const type = match[1];
 
      if (type === "stop") {
        const previous = ranges.at(-1);
 
        // Ignore whole "ignore stop" if no previous start was found
        if (previous && previous.stop === Infinity) {
          previous.stop = lineNumber;
        }
 
        continue;
      }
 
      ranges.push({ start: lineNumber, stop: Infinity });
    }
  }
 
  const ignoredLines = new Set<number>();
 
  for (const range of ranges) {
    for (let line = range.start; line <= range.stop; line++) {
      ignoredLines.add(line);
 
      if (line >= lineNumber) {
        break;
      }
    }
  }
 
  return ignoredLines;
}