UNPKG

diffjam

Version:
96 lines (78 loc) 3.61 kB
/* Finds matches for a given set of `needles` to search for, calling `onMatch` for each match found. The `needles` has a primary regex with the global and multiline flags, so we cycle through each of its matches using `.exec`. Any other regexes in the `needles` are used to further filter whether the first line matched in meets other terms. If this is indeed a match, we compute the line number and column number as well as format the match for display. */ import chalk from "chalk"; import { findLastIndex, last } from "lodash"; import { Match } from "./match"; import { Needles } from "./Policy"; const newLineRegExp = new RegExp("\n", "gm"); export class FileMatcher { private newLineIndexes: number[] = []; constructor(public path: string, public contents: string) { let newLineRegExpMatch: RegExpExecArray | null; while ((newLineRegExpMatch = newLineRegExp.exec(contents))) { this.newLineIndexes.push(newLineRegExpMatch.index); } } findMatches(needles: Needles, onMatch: (match: Match) => void) { let regexMatch: RegExpExecArray | null; while (regexMatch = needles.regex.exec(this.contents)) { const found = regexMatch[0]; const foundStartIndex = regexMatch.index; const foundLength = found.length; const foundEndIndex = foundStartIndex + foundLength; const startLineNumber = this.newLineIndexes.length && ( foundStartIndex > last(this.newLineIndexes)! ? this.newLineIndexes.length : this.newLineIndexes.findIndex((index) => index > foundStartIndex) ) const startLineIndex = startLineNumber === 0 ? 0 : this.newLineIndexes[startLineNumber - 1] + 1; const startLineEndsIndex = this.newLineIndexes[startLineNumber]; const foundOnStartLine = foundEndIndex > startLineEndsIndex ? this.contents.substring(foundStartIndex, startLineEndsIndex) : found; const beforeInSameLine = this.contents.substring(startLineIndex, foundStartIndex); const afterInSameLine = foundEndIndex > startLineEndsIndex ? "" : this.contents.substring(foundStartIndex + foundLength, this.newLineIndexes[startLineNumber] || this.contents.length); const startWholeLine = beforeInSameLine + foundOnStartLine + afterInSameLine; if (needles.negative.some(term => startWholeLine.includes(term))) { continue; } if (!needles.positive.every(term => startWholeLine.includes(term))) { continue; } if (!needles.otherRegexes.every(term => term.test(startWholeLine))) { continue; } const endLineNumber = this.newLineIndexes.length && ( 1 + findLastIndex(this.newLineIndexes, (index) => index < foundEndIndex) ); const endLineIndex = endLineNumber === 0 ? 0 : this.newLineIndexes[endLineNumber - 1] + 1; const startColumn = foundStartIndex - startLineIndex; const endColumn = foundEndIndex - endLineIndex; const startWholeLineFormatted = beforeInSameLine + chalk.bold(foundOnStartLine) + afterInSameLine; const breachPath = `${this.path}(${startLineNumber + 1}:${startColumn + 1})`; onMatch({ startLineNumber, endLineNumber, startColumn, endColumn, found, startWholeLine, startWholeLineFormatted, breachPath, path: this.path, }); } } } export function findMatches(fileContents: string, needles: Needles) { const fileMatcher = new FileMatcher("", fileContents); const matches: Match[] = []; fileMatcher.findMatches(needles, (match) => matches.push(match)); return matches; }